Talvolta le funzioni private sono unità di funzionalità interne ancora da estrarre. Quindi perché non provarli?

9

A volte le funzioni private di un modulo o di una classe sono unità di funzionalità interne ancora da estrarre, che potrebbero meritare i propri test. Quindi perché non provarli? In futuro scriverà per i test se / quando verranno estratti. Quindi, perché non scrivere i test ora, quando fanno ancora parte dello stesso file?

Per dimostrare:

Perprimacosa,hoscrittomodule_a.Oravoglioscriveretestperquesto.Vorreitestarelafunzione'privata'_private_func.Noncapiscoperchénonscriveròuntestperquesto,sepoipotreirifarlocomunquealsuomodulointerno,eallorascriveretestperquesto.

Supponiamocheabbiaunmoduloconleseguentifunzioni(potrebbeancheessereunaclasse):

defpublic_func(a):b=_do_stuff(a)return_do_more_stuff(b)

_do_stuffe_do_more_stuffsonofunzioni'private'delmodulo.

Capiscol'ideachedovremmotestaresolol'interfacciapubblica,nonidettaglidiimplementazione.Tuttavia,eccolacosa:

_do_stuffe_do_more_stuffcontengonolamaggiorpartedellefunzionalitàdelmodulo.Ognunodiessipotrebbeessereunafunzionepubblicadiundiversomodulo"interno". Ma non sono ancora evoluti e abbastanza grandi da essere estratti per separare i file.

Quindi testare queste funzioni sembra giusto perché sono unità di funzionalità importanti. Se fossero in moduli diversi come funzioni pubbliche, li avremmo testati. Quindi, perché non testarli quando non sono ancora (o mai) estratti in un file diverso?

    
posta Aviv Cohn 09.11.2016 - 15:57
fonte

3 risposte

13

La necessità di testare non è la stessa della necessità di essere pubblici.

Il codice non banale deve essere testato indipendentemente dall'esposizione. Il comportamento non pubblico non ha bisogno di esistere e tanto meno di essere testato.

Queste visualizzazioni contrastanti possono indurti a voler rendere pubbliche tutte le funzioni o rifiutare di includere il codice in una funzione, a meno che non sia pubblico.

Questa non è la risposta. Sii disposto a creare funzioni di supporto private. Provali attraverso l'interfaccia pubblica che lo utilizza il più possibile.

Se il test tramite l'interfaccia pubblica non esercita sufficientemente la funzione privata, la funzione privata sta tentando di consentire molto.

La convalida può aiutare a restringere ciò che la funzione privata consente. Se non riesci a passare un null passando per l'interfaccia pubblica, puoi comunque lanciare un'eccezione se ne provi comunque uno.

Perché dovresti? Perché testare ciò che non vedrai mai? Perché le cose cambiano. Potrebbe essere privato ora ma essere pubblico più tardi. Il codice chiamante potrebbe cambiare. Il codice che rifiuta esplicitamente null rende l'uso corretto e lo stato previsto chiaro.

Ovviamente null potrebbe andare bene. È solo un esempio qui. Ma se ti aspetti qualcosa, è utile rendere l'aspettativa chiara.

Potrebbe non essere il tipo di test che avevi in mente, ma spero che sarai disposto a creare funzioni di supporto private quando opportuno.

Il desiderio di testare è buono ma non dovrebbe essere la forza trainante nella progettazione della tua API pubblica. Progettare l'API pubblica per essere facile da usare. Probabilmente non lo sarà se ogni funzione è pubblica. L'API dovrebbe essere qualcosa che le persone possono capire come usare senza immergersi nel codice. Non lasciare queste persone a chiedersi a cosa serva questa strana funzione di supporto.

Nascondere le funzioni di supporto pubblico in un modulo interno è un tentativo di rispettare la necessità di un'API pulita mentre espone gli helper per i test. Non dirò che questo è sbagliato. Potresti fare il primo passo verso un diverso livello architettonico. Ma per favore, padroneggia l'arte di testare le funzioni di aiuto private attraverso le funzioni pubbliche che le usano per prime. In questo modo non utilizzerai più questa soluzione alternativa.

    
risposta data 09.11.2016 - 19:01
fonte
7

Risposta breve: No

Risposta più lunga: sì, ma tramite l''API' pubblica della classe

L'intera idea dei membri privati di una classe è che rappresentano una funzionalità che dovrebbe essere invisibile al di fuori dell '"unità" di codice, per quanto grande si voglia definire quell'unità. Nel codice orientato agli oggetti, quell'unità spesso finisce per essere una classe.

Dovresti avere la tua classe progettata in modo tale che sia possibile richiamare tutte le funzionalità private attraverso varie combinazioni di stato di input. Se trovi che non esiste un modo relativamente semplice per farlo, è probabile che il tuo design richieda maggiore attenzione.

Dopo aver chiarito la questione, questa è solo una questione di semantica. Se il codice in questione può funzionare come un'unità autonoma separata e viene testato come se fosse un codice pubblico, non vedo alcun vantaggio di non spostarlo in un modulo autonomo. Al momento, serve solo a confondere i futuri sviluppatori (incluso te stesso, tra 6 mesi), sul motivo per cui il codice apparentemente pubblico è nascosto all'interno di un altro modulo.

    
risposta data 09.11.2016 - 16:21
fonte
5

L'intero punto delle funzioni private è che sono dettagli di implementazione nascosti che possono essere modificati a piacimento, senza modificare l'API pubblica. Per il tuo codice di esempio:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

Se hai una serie di test che usano solo public_func , se la riscrivi in:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

quindi, finché il risultato di ritorno per un particolare valore di a rimane lo stesso, allora tutti i tuoi test saranno positivi. Se il risultato del reso cambia, un test fallirà, evidenziando il fatto che qualcosa si è rotto.

Questa è una buona cosa: API pubblica statica; lavorazioni interne ben incapsulate; e test robusti.

Tuttavia, se avessi scritto test per _do_stuff o _do_more_stuff e avessi apportato la suddetta modifica, ora avresti un sacco di test non funzionanti, non perché la funzionalità fosse cambiata, ma perché l'implementazione di tale funzionalità cambiato. Quei test avrebbero bisogno di riscrittura per funzionare con le nuove funzioni, ma avendo loro lavorato, tutto quello che sapresti è che quei test hanno funzionato con le nuove funzioni. Avresti perso i test originali e quindi non sapresti se il comportamento di public_func è cambiato e quindi i test sarebbero praticamente inutili.

Questa è una cosa negativa: un'API che cambia; lavorazioni interne esposte strettamente accoppiate alle prove; e test fragili che cambiano non appena vengono apportate modifiche all'implementazione.

Quindi no, non testare le funzioni private. Mai.

    
risposta data 09.11.2016 - 16:17
fonte

Leggi altre domande sui tag