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


9

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

Dimostrare:

inserisci qui la descrizione dell'immagine

Prima ho scritto module_a. Ora voglio scrivere dei test per questo. Vorrei testare la funzione "privata" _private_func. Non capisco perché non scriverei un test per esso, se in seguito potessi rifattorizzarlo sul suo modulo interno e poi scrivere test per esso.


Supponiamo che io abbia un modulo con le seguenti funzioni (potrebbe anche essere una classe):

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

_do_stuffe _do_more_stuffsono funzioni "private" del modulo.

Comprendo l'idea che dovremmo testare solo l'interfaccia pubblica, non i dettagli di implementazione. Tuttavia, ecco la cosa:

_do_stuffe _do_more_stuffcontiene la maggior parte delle funzionalità del modulo. Ognuno di essi potrebbe essere una funzione pubblica di un diverso modulo "interno". Ma non sono ancora evoluti e abbastanza grandi per essere estratti in file separati.

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



2
"I metodi privati ​​sono vantaggiosi per i test unitari ..." "... attenersi al metodo privato mi porta a un miglioramento utile e affidabile nei test unitari. Al contrario, indebolire le limitazioni di accesso" per testabilità "mi dà solo un oscuro, difficile da capire pezzo di codice di prova, che è inoltre a rischio permanente di essere rotto da qualsiasi refactoring minore; francamente quello che ottengo sembra sospettosamente un debito tecnico "
moscerino del

3
"Devo testare le funzioni private?" No. Mai, mai, mai.
David Arno,

2
@DavidArno Perché? Qual è l'alternativa ai test interni? Solo test di integrazione? O rendere più pubbliche le cose? (anche se nella mia esperienza collaudo principalmente metodi pubblici su classi interne, non metodi privati)
CodesInChaos

1
Se è abbastanza importante che sia necessario scrivere test per questo, allora dovrebbe già essere in un modulo separato. In caso contrario, si verifica il suo comportamento utilizzando l'API pubblica.
Vincent Savard,

Risposte:


14

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

Il codice non banale deve essere testato indipendentemente dall'esposizione. Non è necessario che esistano comportamenti non pubblici e tanto meno testati.

Queste viste contrastanti possono portarti a voler rendere pubblica ogni funzione o rifiutare di scomporre il codice in una funzione a meno che non sia pubblico.

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

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

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

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

Naturalmente null potrebbe andare bene. È solo un esempio qui. Ma se ti aspetti qualcosa, è utile chiarire le aspettative.

Potrebbe non essere il tipo di test che avevi in ​​mente, ma speriamo che tu sia disposto a creare funzioni di aiuto private quando appropriato.

Il desiderio di testare è buono ma non dovrebbe essere la forza trainante nella progettazione della tua API pubblica. Progetta l'API pubblica in modo che sia 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 che queste persone si chiedano a cosa serva questa strana funzione di aiuto.

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 è 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 userai eccessivamente questa soluzione alternativa.


Ho trovato un approccio, mi piacerebbe sentire la tua opinione: ogni volta che mi trovo in una situazione in cui voglio testare una funzione privata, verificherò se riesco a testarla sufficientemente attraverso una delle funzioni pubbliche (compresi tutti i casi limite, ecc.). Se posso, non scriverò un test per questa funzione in modo specifico, ma lo testerò solo testando le funzioni pubbliche che lo utilizzano. Tuttavia, se ritengo che la funzione non possa essere sufficientemente testata attraverso l'interfaccia pubblica e meriti un test a sé stante, la estrarrò in un modulo interno in cui il suo pubblico e scriverò i test per essa. Cosa ne pensi?
Aviv Cohn,

Dirò che sto chiedendo la stessa cosa agli altri ragazzi che hanno risposto qui :) Mi piacerebbe sentire l'opinione di tutti.
Aviv Cohn,

Ancora una volta, non ti dirò di no. Temo che tu non abbia detto nulla riguardo al modo in cui ciò influisce sull'usabilità. La differenza tra pubblico e privato non è strutturale. È uso. Se la differenza tra pubblico e privato è una porta d'ingresso e una porta sul retro, allora il tuo lavoro è quello di costruire una tettoia nel cortile sul retro. Belle. Finché le persone non si perdono laggiù.
candied_orange,

1
Eseguito l'upgrade per "Se i test attraverso l'interfaccia pubblica non esercitano sufficientemente la funzione privata, la funzione privata sta cercando di consentire molto."
Kris Welsh

7

Risposta breve: No

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

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

La classe dovrebbe essere 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, probabilmente suggerisce che il tuo progetto necessita di maggiore attenzione.


Dopo il chiarimento della domanda, 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 nel non spostarlo in un modulo autonomo. Al momento, serve solo a confondere i futuri sviluppatori (incluso te stesso, tra 6 mesi), sul perché il codice apparentemente pubblico è nascosto all'interno di un altro modulo.


Ciao, grazie per la risposta :) Per favore rileggi la domanda, ho modificato per chiarire.
Aviv Cohn,

Ho trovato un approccio, mi piacerebbe sentire la tua opinione: ogni volta che mi trovo in una situazione in cui voglio testare una funzione privata, verificherò se riesco a testarla sufficientemente attraverso una delle funzioni pubbliche (compresi tutti i casi limite, ecc.). Se posso, non scriverò un test per questa funzione in modo specifico, ma lo testerò solo testando le funzioni pubbliche che lo utilizzano. Tuttavia, se ritengo che la funzione non possa essere sufficientemente testata attraverso l'interfaccia pubblica e meriti un test a sé stante, la estrarrò in un modulo interno in cui il suo pubblico e scriverò i test per essa. Cosa ne pensi?
Aviv Cohn,

Dirò che sto chiedendo la stessa cosa agli altri ragazzi che hanno risposto qui :) Mi piacerebbe sentire l'opinione di tutti.
Aviv Cohn,

5

Il punto centrale delle funzioni private è che sono dettagli di implementazione nascosti che possono essere modificati a piacimento, senza cambiare 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, allora se lo riscrivi a:

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

quindi, fintanto che il risultato di ritorno per un determinato valore di arimane lo stesso, tutti i test saranno validi. Se il risultato di ritorno cambia, un test fallirà, evidenziando il fatto che qualcosa si è rotto.

Questa è una cosa positiva: API pubblica statica; meccanismi interni ben incapsulati; e test solidi.

Tuttavia, se avessi scritto dei test per _do_stuffo _do_more_stuffe successivamente apportato la modifica di cui sopra, ora avresti un sacco di test interrotti, non perché la funzionalità è cambiata, ma perché l'implementazione di tale funzionalità è cambiata. Quei test avrebbero bisogno di essere riscritti per funzionare con le nuove funzioni, ma dopo averli fatti funzionare, tutto ciò 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_funccambiato e quindi i test sarebbero sostanzialmente inutili.

Questa è una brutta cosa: un'API che cambia; meccanismi interni esposti strettamente accoppiati a prove; e test fragili che cambiano non appena vengono apportate modifiche all'implementazione.

Quindi no, non testare le funzioni private. Mai.


Ciao David, grazie per la tua risposta. Per favore, rileggi la mia domanda, ho modificato per chiarire.
Aviv Cohn,

Meh. Non c'è niente di sbagliato nel testare le funzioni interne. Personalmente non scrivo una funzione non banale di alcun tipo a meno che non riesca a coprirla con un paio di unit test, quindi vietare i test sulle funzioni interne mi impedirebbe praticamente di scrivere funzioni interne, privandomi di una tecnica molto utile. Le API possono e cambiano; quando lo fanno, è necessario modificare i test. Il refactoring di una funzione interna (e il suo test) non interrompe i test della funzione esterna; questo è il punto di avere quei test. Pessimo consiglio nel complesso.
Robert Harvey,

Quello che stai davvero sostenendo è che non dovresti avere funzioni private.
Robert Harvey,

1
@AvivCohn Sono entrambi abbastanza grandi da giustificare i test, nel qual caso sono abbastanza grandi da ottenere il proprio file; o sono abbastanza piccoli da non doverli testare separatamente. Quindi che cos'è?
Doval,

3
@RobertHarvey No, rende l'argomento "suddividere le grandi classi in componenti allentati se necessario". Se non sono davvero pubblici, è probabilmente un buon caso d'uso per la visibilità privata dei pacchetti.
Doval,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.