Come evitare la necessità di testare l'Unità metodi privati

9

So che non dovresti testare metodi privati, e se sembra che tu ne abbia bisogno, potrebbe esserci una classe in attesa di venire fuori.

Ma non voglio avere lezioni di gazillion solo per poter testare le loro interfacce pubbliche e trovo che per molte classi, se provo solo i metodi pubblici, finisco per dover prendere in giro molte dipendenze e i test unitari sono enormi e difficili da seguire.

Preferisco di gran lunga prendere in giro i metodi privati durante il test di quelli pubblici e prendere in giro le dipendenze esterne durante il test di quelle private.

Sono pazzo?

    
posta Fran Sevillano 25.07.2018 - 16:10
fonte

7 risposte

20

Hai parzialmente ragione - non dovresti direttamente testare metodi privati. I metodi privati su una classe dovrebbero essere invocati da uno o più metodi pubblici (forse indirettamente - un metodo privato chiamato da un metodo pubblico può richiamare altri metodi privati). Pertanto, durante il test dei metodi pubblici, testerai anche i tuoi metodi privati. Se i metodi privati non sono stati testati, i casi di test sono insufficienti oppure i metodi privati non sono utilizzati e possono essere rimossi.

Se stai adottando un approccio di test white-box, dovresti considerare i dettagli di implementazione dei tuoi metodi privati quando costruisci test unitari attorno ai tuoi metodi pubblici. Se stai adottando un approccio black-box, non dovresti testare i dettagli di implementazione nei metodi pubblici o privati, ma contro il comportamento previsto.

Personalmente, preferisco un approccio white-box ai test unitari. Posso creare test per mettere i metodi e le classi sotto test in stati diversi che causano comportamenti interessanti nei miei metodi pubblici e privati e poi affermare che i risultati sono quelli che mi aspetto.

Quindi - non prendere in giro i tuoi metodi privati. Usali per capire cosa è necessario testare per fornire una buona copertura della funzionalità che fornisci. Questo è particolarmente vero al livello di test unitario.

    
risposta data 25.07.2018 - 16:59
fonte
3

Penso che questa sia una pessima idea.

Il problema con la creazione di test unitari di membri privati è che si adatta male al ciclo di vita del prodotto.

La ragione per cui hai scelto di rendere privati quei metodi è che non sono centrali in ciò che le tue classi stanno cercando di fare - solo aiutanti nel modo in cui stai implementando quella funzionalità. Come refactoring, questi dettagli privati sono probabilmente candidati a cambiare e causeranno attriti con il refactoring.

Inoltre, una differenza fondamentale tra membri pubblici e privati è che dovresti pensare attentamente alla tua API pubblica, documentarla bene e verificarla bene (asserzioni, ecc.). Ma con un'API privata, sarebbe inutile pensare con cautela (uno sforzo inutile dato che il suo uso è così localizzato). Mettere i metodi privati in unit test equivale a creare dipendenze esterne su quei metodi. Il che significa che l'API deve essere stabile e ben documentata (dal momento che qualcuno deve capire perché quei test unitari hanno fallito se / quando lo fanno).

Ti suggerisco:

  • RETHINK se questi metodi dovrebbero essere privati
  • Scrivi test onetime (solo per assicurarti che sia corretto ora, rendilo pubblico temporaneamente in modo da poter testare e quindi eliminare il test)
  • Esecuzione di test condizionali nella tua implementazione utilizzando #ifdef e asserzioni (i test vengono eseguiti solo nelle build di debug).

Apprezziamo la tua inclinazione a testare questa funzionalità e che è difficile testarla tramite la tua attuale API pubblica. Ma personalmente considero la modularità più che la copertura dei test.

    
risposta data 25.07.2018 - 16:35
fonte
3

Gli UnitTest testano il comportamento pubblico osservabile , non il codice, dove "pubblico" significa: restituisce valori e comunica con le dipendenze.

Una "unità" è un codice qualsiasi, che risolve lo stesso problema (o più precisamente: ha lo stesso motivo per cambiare). Potrebbe essere un singolo metodo o un gruppo di classi.

Il motivo principale per cui non vuoi testare private methods è: sono dettagli di implementazione e potresti volere cambiarli durante refactoring (migliorare il tuo codice applicando i principi OO senza modificare la funzionalità). Questo è esattamente quando non vuoi che le tue unittests cambino in modo che possano garantire che il comportamento del tuo CuT non sia cambiato durante il refactoring.

But, I don't want to have a gazillion classes just so that I can test their public interfaces and I find that for many classes if I just test the public methods I end up having to mock a lot of dependencies and the unit tests are enormous and hard to follow.

Di solito provo il contrario: più le classi sono piccole (meno responsabilità hanno) meno dipendenze hanno, e più facili sono le unittest sia, sia per scrivere e leggere.

Idealmente applica lo schema stesso livello di astrazione alle tue classi. Questo significa che le tue classi forniscono una certa logica di business (preferibilmente come "pure funzioni" che lavorano solo sui loro parametri senza mantenere uno stato) (x) o metodi di chiamata su altri oggetti, non entrambi allo stesso tempo .

In questo modo l'unittesting del comportamento aziendale è un gioco da ragazzi e gli oggetti "delegazione" di solito sono troppo semplici per fallire (nessuna ramificazione, nessuna modifica dello stato) in modo che non sia necessaria alcuna unittest e il loro test può essere lasciato a integration o module test

    
risposta data 25.07.2018 - 21:53
fonte
1

Il test delle unità ha un certo valore quando sei l'unico programmatore che lavora sul codice, specialmente se l'applicazione è molto grande o molto complessa. Dove il test unitario diventa essenziale è quando si ha un numero maggiore di programmatori che lavorano sulla stessa base di codice. Il concetto di unit test è stato introdotto per affrontare alcune delle difficoltà di lavoro in questi team più grandi.

Il motivo per cui i test unitari aiutano i team più grandi a fare affidamento sui contratti. Se il mio codice sta effettuando chiamate a un codice scritto da qualcun altro, sto facendo delle supposizioni su ciò che il codice dell'altra persona sta per fare in varie situazioni. A condizione che queste supposizioni siano ancora vere, il mio codice funzionerà ancora, ma come faccio a sapere quali ipotesi sono valide e come faccio a sapere quando tali ipotesi sono cambiate?

È qui che entrano i test unitari. L'autore di una classe crea test unitari per documentare il comportamento previsto della loro classe. Il test unitario definisce tutti i modi validi per utilizzare la classe, e l'esecuzione del test dell'unità convalida che questi casi d'uso funzionano come previsto. Un altro programmatore che vuole fare uso della tua classe può leggere i tuoi test unitari per capire il comportamento che possono aspettarsi per la tua classe, e usarlo come base per le loro ipotesi su come funziona la tua classe.

In questo modo le firme del metodo pubblico della classe e dei test unitari formano insieme un contratto tra l'autore della classe e gli altri programmatori che fanno uso di questa classe nel loro codice.

In questo scenario cosa succede se includi test di metodi privati? Chiaramente non ha senso.

Se sei l'unico programmatore che lavora sul tuo codice e vuoi usare il test dell'unità come metodo per eseguire il debug del tuo codice allora non vedo alcun danno in questo, è solo uno strumento e puoi usarlo in ogni modo questo funziona per te, ma non è stato il motivo per cui è stato introdotto il test delle unità e non offre i principali vantaggi dei test unitari.

    
risposta data 28.07.2018 - 22:40
fonte
1

Prima di rispondere a una domanda del genere, devi decidere cosa vuoi raggiungere.

Scrivi il codice. Speri che rispetti il suo contratto (in altre parole, fa quello che dovrebbe fare. Annotare ciò che dovrebbe fare è un enorme passo avanti per alcune persone).

Per essere ragionevolmente convinto che il codice faccia ciò che deve fare, lo fissi abbastanza a lungo, o scrivi un codice di test che verifica un numero sufficiente di casi per convincerti "se il codice supera tutti questi test, allora è corretto ".

Spesso ti interessa solo l'interfaccia definita pubblicamente di alcuni codici. Se utilizzo la tua libreria, non mi interessa come hai fatto a farlo funzionare correttamente, solo che fa funziona correttamente. Verifico che la tua libreria sia corretta eseguendo i test unitari.

Ma stai creando la libreria. Farlo funzionare correttamente può essere difficile da raggiungere. Diciamo che mi interessa solo che la libreria esegua correttamente l'operazione X, quindi ho un test unitario per X. Tu, lo sviluppatore responsabile della creazione della libreria, implementa X combinando i passaggi A, B e C, che sono totalmente non banali. Per far funzionare la tua libreria, aggiungi dei test per verificare che A, B e C funzionino correttamente. Vuoi questi test? Dire "non dovresti avere test unitari per metodi privati" è abbastanza inutile. Tu vuoi test per questi metodi privati. Forse qualcuno ti dice che l'unità che testa i metodi privati è sbagliata. Ma questo significa solo che potresti non chiamarli "unit test" ma "test privati" o qualsiasi altra cosa ti piaccia chiamarli.

Il linguaggio Swift risolve il problema che non vuoi esporre A, B, C come metodi pubblici solo perché vuoi testarlo dando alle funzioni un attributo "testabile". Il compilatore consente di chiamare i metodi testabili privati dai test unitari, ma non dal codice non test.

    
risposta data 29.07.2018 - 01:33
fonte
0

Sì, sei pazzo .... COME UNA VOLPE!

Ci sono un paio di modi per testare metodi privati, alcuni dei quali sono dipendenti dalla lingua.

  • riflessione! le regole sono fatte per essere infrante!
  • rendili protetti, ereditali e sostituiscili
  • amico / InternalsVisibleTo classi

Nel complesso però se vuoi testare metodi privati, probabilmente vuoi trasferirli su metodi pubblici su una dipendenza e testarli / iniettarli.

    
risposta data 25.07.2018 - 16:15
fonte
0

Rimani pragmatico. Testare casi speciali in metodi privati impostando lo stato dell'istanza e i parametri su un metodo pubblico in modo che tali casi avvengano lì, è spesso troppo complicato.

Aggiungo un extra internal accessor (insieme al flag InternalsVisibleTo dell'assembly test), chiaramente chiamato DoSomethingForTesting(parameters) , per testare quei metodi "privati".

Ovviamente, l'implementazione potrebbe cambiare in qualsiasi momento e quei test inclusi gli accessors di test diventano obsoleti. È ancora meglio dei casi non verificati o dei test illeggibili.

    
risposta data 30.07.2018 - 11:41
fonte

Leggi altre domande sui tag