2012-01-25 8 views
11

Quindi sono estremamente nuovo nei test del software e sto cercando di aggiungere un paio di test a una delle mie applicazioni. Ho un metodo pubblico addKeywords() che lungo la strada chiama un metodo privato removeInvalidOperations(). Questo metodo privato effettua chiamate a un'API esterna ed è composto da ~ 50 righe di codice. Poiché considero questo metodo un po 'complesso, vorrei testarlo senza doverlo fare chiamando il metodo addKeyword(). Eppure questo non sembra possibile (almeno non con JUnit).Voglio testare un metodo privato - c'è qualcosa di sbagliato nel mio design?

Le informazioni che ho visto suggeriscono che il desiderio di testare i propri metodi privati ​​potrebbe essere un odore di codice. Alcune persone suggeriscono che potrebbe essere un segno che questo dovrebbe essere rifattorizzato in una classe separata e reso pubblico. Inoltre, vi sono i suggerimenti che, se è davvero necessario, è possibile apportare modifiche al codice di produzione, ad es. cambia la visibilità del metodo privato.

Non vedo davvero perché ho un problema con il mio attuale design di codice, ma non mi piace anche l'idea di modificare il mio codice di produzione per soddisfare le mie esigenze di test.

Come ho detto, sono piuttosto nuovo per i test, quindi qualsiasi aiuto è molto apprezzato. Inoltre, per favore fatemi sapere se ci sono ulteriori informazioni che posso fornire per aiutare con le risposte.

+0

Una soluzione rapida che ho spesso utilizzato è quella di modificare l'accesso da privato a predefinito (pacchetto) e commentare l'accesso al pacchetto // // solo per i test di unità. Se questo è accettabile dipende dagli standard di codifica e da quanto ci si fida i tuoi colleghi programmatori per capire che questo metodo, anche se l'accesso al pacchetto, non è una "vera API". – user949300

risposta

13

Suggerisco di rifarlo.

Le informazioni che ho guardato è suggerendo che il desiderio di testare quelli metodi privati ​​potrebbe essere un odore di codice. Alcune persone suggeriscono che potrebbe essere un segno che questo dovrebbe essere refactored in una classe separata e reso pubblico.

Hai coperto le varie ragioni a favore e contro di esso nella tua stessa domanda, sembri essere ben consapevole degli argomenti. Ma hai un metodo che sembra piuttosto complicato e implica un'API esterna. Vale la pena provare da solo.removeInvalidOperations() può ancora essere un metodo privato sulla classe in cui si trova, ma sostanzialmente delegherebbe a un'altra dipendenza.

class YourClass 
{ 
    private OperationRemover remover; 

    public void addKeywords() { 
     // whatever 
     removeInvalidOperations(); 
    } 

    private void removeInvalidOperations() { 
     remover.remove(); 
    } 
} 

Questo ti dà il vantaggio di essere in grado di sostituire questa dipendenza ad un certo punto, tra cui essere in grado di testare il metodo di addKeywords() senza realmente effettuare una chiamata API esterna, il che renderebbe il test che il metodo più semplice.OperationRemover potrebbe essere un'interfaccia, ad esempio, e per scopi di test, si passa semplicemente uno stub al suo posto invece della versione concreta utilizzata nella produzione. Per quanto riguarda la versione concreta, è possibile scrivere test indipendentemente da ciò che sta accadendo con la classe esistente.

Io in realtà non vedo perché ho un problema con il mio codice di progettazione corrente, ma anche non mi piace l'idea di modificare il mio codice di produzione per soddisfare le mie esigenze test.

La facilità di testabilità è un vantaggio secondario. Guardalo in un altro modo: quello che stai facendo in realtà è rendere il codice liberamente accoppiato ed estensibile. In alto, abbiamo separato la chiamata all'API esterna dal codice che potrebbe essere necessario utilizzare il risultato. L'API esterna potrebbe cambiare. Potresti passare da un servizio all'altro, ma il codice che utilizza il risultato non deve preoccuparsi. Quella classe può rimanere la stessa, solo la classe che effettivamente pone le chiamate deve essere modificata (o sostituita).

Esempio di mondo reale: l'anno è il 2007 e si lavora per una banca in un grande centro finanziario negli Stati Uniti. La tua applicazione deve utilizzare le informazioni dell'account. Il tuo codice raggiunge un servizio web di qualche tipo all'interno della banca e ottiene le informazioni di cui ha bisogno, nella forma di cui ha bisogno, quindi procede al suo trattamento. Nel 2008, il settore finanziario statunitense implode e la tua banca (che è sull'orlo del collasso) viene divorata da un'altra banca. La tua applicazione è stata risparmiata, ma ora devi raggiungere una diversa API già esistente nella banca sopravvissuta per ottenere informazioni sull'account da lì. Il codice che consuma queste informazioni sull'account deve cambiare? Non necessariamente. Sono le stesse informazioni sull'account di prima, solo da una fonte diversa. No, tutto ciò che deve essere modificato è l'implementazione che richiama le API. Il codice del consumo non deve mai sapere.

Il fatto che un accoppiamento così libero promuova e faciliti i test è un vantaggio.

+0

Grazie per una risposta così dettagliata! Questo mi ha davvero aiutato a capire alcuni dei vantaggi dietro questo approccio. – QuakerOat

7

Se è private, non può essere considerato parte dell'API della tua applicazione, quindi testarlo è davvero un odore di codice - quando il test si interrompe, è OK o no?

I test di unità devono essere orientati alle funzionalità, non orientati al codice. Provi unità di funzionalità, non unità di codice.

Indipendentemente dalla filosofia, una classe esterna all'implementazione non può accedere al metodo privato senza l'hacking della JVM, quindi sei sfortunato: devi cambiare la visibilità del metodo, creare un'API di test protected la classe si estende o verifica indirettamente la funzione chiamando i metodi pubblici che ne fanno uso.

0

Se non si desidera chiamare addKeywords(), forse si dovrebbe semplicemente aggiungere un altro metodo pubblico testRemoveInvalidOperations(), che chiama semplicemente il numero privato removeInvalidOperations(). È possibile rimuovere il test in un secondo momento.

+0

+1 Non è una cattiva idea. Modifica minore: crea l'accesso predefinito (pacchetto) testRemoveInvalidOperations() e aggiungi un commento pertinente per chiarire che lo scopo è solo per i test unitari. – user949300

+0

-1 Non è mai una buona idea fare buchi nel codice solo per testare. Devi testare quello che hai, non quello che vuoi avere. Inoltre, i buoni test tendono a richiedere un buon design. Se il test vuole che tu lo rifatti, probabilmente ha ragione. E provare un pezzo di codice complicato è più che giusto. – pkoch

+0

Certo, devi assicurarti di eliminare il codice di prova prima di rilasciare i file al pubblico. Lasciare pubblico il codice è una cattiva idea! – Daniel

4

In genere non si desidera testare un metodo privato, ma esistono delle eccezioni.

Si potrebbe essere tentati di provare un metodo privato se:

  1. Non hai pensato attentamente su come testare il metodo privato indirettamente chiamando i metodi pubblici esistenti.

  2. L'API della tua classe è troppo rigida. I metodi pubblici richiedono più parametri o alcuni dei metodi privati ​​devono essere resi pubblici.

  3. API La classe è abbastanza flessibile, ma sotto i pubblici metodi ha alcuni metodi privati ​​piuttosto complicati in corso sotto.

In base alla tua domanda, potresti trovarti in uno di questi casi.

Per (1), ovviamente si dovrebbe prima provare a trovare un modo per testare il proprio metodo privato con i metodi pubblici esistenti.

Per (2) e (3), i test di unità non ti diranno in quale caso ti trovi. Quello che devi fare è scrivere un codice di esempio. Come Josh Bloch recommends, codifica alcuni casi d'uso per la tua API. La tua API dovrebbe essere l'insieme minimo di metodi pubblici richiesti per soddisfare i tuoi casi d'uso.

(3) è il caso in cui è OK testare metodi privati. Ci sono vari tricks for that. Per il codice di produzione, è meglio che esporre il tuo metodo all'utente API (rendendolo pubblico) solo per poterlo testare. O dividere le funzionalità correlate in 2 classi solo per poterle testare.

Invece di pensare in termini di "codice odori", che è impreciso e soggettivo, si può pensare in termini di information hiding. Le decisioni di progettazione che potrebbero cambiare non dovrebbero essere esposte nella tua API pubblica. Preferibilmente, le decisioni di progettazione che sono suscettibili di cambiamento non dovrebbero essere esposte nemmeno ai test delle unità: ecco perché le persone raccomandano di non testare metodi privati.

Ma se pensi davvero che sia importante testare l'unità del tuo metodo privato, e se non puoi farlo adeguatamente tramite i metodi pubblici, non sacrificare la correttezza del tuo codice! Prova il metodo privato. Il caso peggiore è che il codice di test è più confuso e che è necessario riscrivere i test quando il metodo privato cambia.

Problemi correlati