junit & java: test di metodi non pubblici

JUnit testerà solo quei metodi nella mia class che sono pubblici. Come si eseguono i test di junit su quelli che non sono (cioè privati, protetti)?

Posso testarli non usando junit, ma mi chiedevo quale fosse il metodo standard di junit.

Una scuola di pensiero sui test unitari dice che dovresti solo essere in grado di testare metodi pubblici, perché dovresti solo testare unitamente la tua API pubblica e, così facendo, dovresti coprire il codice con metodi non pubblici. Il tuo chilometraggio può variare; Trovo che questo a volte sia il caso e a volte no.

Detto questo, ci sono un paio di modi per testare metodi non pubblici:

  • È ansible testare i metodi protetti e per l’ambito del pacchetto inserendo i test dell’unità nello stesso pacchetto delle classi che stanno testando. Questa è una pratica abbastanza comune.
  • È ansible testare metodi protetti dai test unitari in un altro pacchetto creando una sottoclass della class sottoposta a test che sovrascrive i metodi che si desidera testare come pubblici, e i metodi sovrascritti chiamano i metodi originali con la parola chiave super. In genere, questa “sottoclass di test” sarebbe una class interna nella class JUnit TestCase che esegue il test. Questo è un po ‘più hacky, secondo me, ma l’ho fatto.

Spero che questo ti aiuti.

Come con molti problemi di test delle unità, testare metodi privati ​​è in realtà un problema di progettazione sotto mentite spoglie. Piuttosto che cercare di fare qualcosa di difficile per testare metodi privati, quando mi trovo a desiderare di scrivere test per metodi privati, mi prendo un minuto per chiedermi: “Come avrei bisogno di progettarlo per poterlo testare a fondo con metodi pubblici?”

Se ciò non funziona, JUnitX consente di testare metodi privati, anche se credo che sia disponibile solo per JUnit 3.8.

Quando scrivi un test JUnit, devi fare un sottile cambiamento di mente: “Sono un cliente della mia class ora”. Ciò significa che il privato è privato e tu metti alla prova solo il comportamento che vede il cliente.

Se il metodo dovesse essere davvero privato, lo considererei un difetto di progettazione per renderlo visibile solo per motivi di testing. Devi essere in grado di dedurne l’operazione corretta in base a ciò che vede il cliente.

Nei tre anni trascorsi da quando ho originariamente scritto questo, ho iniziato ad affrontare il problema in modo leggermente diverso, usando la riflessione Java.

Lo sporco piccolo segreto è che puoi testare i metodi privati ​​in JUnit proprio come faresti di pubblico, usando la riflessione. Puoi testare il contenuto del tuo cuore e non esporlo ancora come pubblico ai clienti.

La soluzione più semplice consiste nel mettere i test JUnit nello stesso pacchetto (ma in una directory diversa) e utilizzare la visibilità predefinita (es. Pacchetto-privato) per i metodi.

Un altro approccio più complicato è l’uso della riflessione per accedere ai metodi privati.

Se si dispone di una quantità significativa di logica sepolta da relativamente pochi punti di ingresso “pubblici”, si sta probabilmente violando il principio di responsabilità unica . Se ansible, ti consigliamo di rifattorizzare il codice in più classi, portando infine a più metodi “pubblici” da cui testare.

Ecco il “probabilmente non dovrebbe farlo in questo modo” metodo che tutti gli altri continuano a insistere su di te. Penso che sia certamente nel campo delle possibilità che ci sono ragioni per farlo in questo modo, però. Il codice seguente accederà a un campo privato, ma il codice per un metodo privato è quasi identico.

public void testPrivateField() throws InterruptedException { Class clazz = ClassWPrivateField.class; try { Field privateField = clazz.getDeclaredField("nameOfPrivateField"); privateField.setAccessible(true); // This is the line // do stuff } catch(NoSuchFieldException nsfe) { nsfe.printStackTrace(); fail(); } catch(IllegalAccessException iae) { iae.printStackTrace(); fail(); } } 

Uso quasi sempre Spring nei miei progetti Java e, come tale, i miei oggetti sono costruiti per l’iniezione delle dipendenze. Tendono ad essere implementazioni abbastanza granulari di interfacce pubbliche che vengono assemblate all’interno del contesto dell’applicazione. Come tale, raramente (se mai) ho la necessità di testare metodi privati ​​perché la class stessa è abbastanza piccola da non essere un problema.

Anche quando non uso Spring, tendo ad adottare le stesse pratiche di assemblare oggetti piccoli e semplici in astrazioni sempre più grandi, ognuna delle quali è relativamente semplice ma resa complessa dagli oggetti aggregati.

Secondo la mia esperienza, la necessità di testare unitamente i metodi privati ​​è un indicatore del fatto che ciò che stai provando potrebbe (e dovrebbe) essere semplificato.

Detto questo, se senti ancora veramente il bisogno:

  • I metodi protetti possono essere testati da sottoclassi;
  • I metodi privati ​​del pacchetto possono essere testati mettendo i test unitari nello stesso pacchetto; e
  • I metodi privati ​​possono essere testati unitamente fornendo, ad esempio, un metodo di proxy factory factory del pacchetto. Non ideale, ma privato significa privato.

Solitamente non testate metodi privati ​​perché possono essere (normalmente) testati indirettamente attraverso un altro metodo pubblico. Quando si esegue un test di guida e si effettuano metodi privati, di solito sono il risultato di un refactoring di “metodo di estrazione” e sono già testati indirettamente.

Se sei preoccupato di testare un metodo privato con molta logica, la cosa più intelligente che potresti fare è spostare quel codice in un’altra class in un metodo pubblico. Una volta fatto ciò, il metodo precedente che utilizzava questo codice può essere semplificato con la funzionalità fornita da uno stub o un mock.

Questo non è davvero pubblicato come risposta, più che ho incontrato lo stesso problema, e il “se deve essere privato che probabilmente dovrebbe essere rifattorizzato” non si adatta a me.

Supponiamo che tu abbia una sorta di funzionalità che vuoi separare in qualche modo dalla class. Ad esempio, supponiamo di avere qualcosa di simile a questo:

 public class HolderOfSomeStrings{ private List internal_values; public List get() { List out = new ArrayList(); for (String s:internal_values) { out.add(process(s)); } return get; } private static String process(String input) { //do something complicated here that other classs shouldn't be interested in } } 

Il punto qui è che junit mi obbliga a rendere il processo pubblico, o almeno protetto, oa inserirlo nella sua class di utilità. Ma se si tratta di una sorta di logica interna di HolderOfSomeStrings, non mi è del tutto chiaro che sia corretto – mi sembra che questo dovrebbe essere privato e renderlo più visibile in qualche modo.

Per prendere in prestito un pensiero da Andy Hunt, anche i tuoi metodi privati ​​devono avere qualche effetto collaterale a cui sei interessato. In altre parole, devono essere richiamati da un metodo pubblico ed eseguire un compito interessante che fa cambiare lo stato del tuo object . Prova per quel cambiamento di stato.

Supponiamo di avere un metodo pubblico pubMethod e un metodo privato privMethod. Quando chiami pubMethod, a sua volta chiama privMethod per eseguire un’attività (magari analizzando una stringa). PubMethod utilizza quindi questa stringa analizzata per impostare i valori delle variabili membro in qualche modo o per influenzare il proprio valore di ritorno. Esegui il test osservando l’effetto desiderato sul valore di ritorno di pubMethod o sulle variabili membro (possibilmente utilizzando gli accessor per raggiungerli).

DP4j Jar

Per testare metodi privati, dobbiamo usare la riflessione e il suo punto in tutte le risposte.

bene ora questo compito è semplificato con l’aiuto di Dp4j jar.

  • Dp4j analizza il tuo codice e genera automaticamente il codice API di Reflection per te.

  • Basta aggiungere dp4j.jar al tuo CLASSPATH.

  • Dp4j.jar contiene i processori di annotazione, cercheranno i metodi nel tuo codice che sono annotati con l’annotazione @Test JUnit.

  • Dp4j analizza il codice di questi metodi e, se trova che stai accedendo illegalmente a metodi privati, sostituirà il tuo riferimento al metodo privato non valido con codice equivalente che utilizza l’API Reflection di Java.

Ottieni maggiori dettagli qui

È ansible utilizzare TestNG anziché JUnit, che non si preoccupa del fatto che il metodo sia privato o pubblico.

Usa la riflessione come sopra per testare i metodi privati. Se stiamo seguendo TDD dovremmo testare metodi privati ​​dato che TDD implica che non ci sarebbero sorprese in seguito. Quindi non si dovrebbe aspettare di finire il suo metodo pubblico per testare i privati. E questo aiuta a test di regressione più granulari al re factoring.

Cerca “PrivateAccessor.invoke”. Il mio codice lo importa da “junitx.util”, ma non so da dove provenga.

In accordo con quasi tutti i post – probabilmente dovresti refactoring e probabilmente non testare privati ​​se non attraverso il pubblico, volevo solo aggiungere un modo diverso di pensarci …

Pensa alla tua class come a una “Unità”, non a un metodo. Stai testando la class e puoi mantenere uno stato valido indipendentemente da come vengono chiamati i metodi pubblici.

Chiamare metodi privati ​​può distruggere l’incapsulamento e in realtà invalidare i test.

Come una specie di propaggine di questo, e non sono sicuro di dove tutti vengano a conoscenza dell’intero problema di “programmazione poliglotta”, ma i test di Groovy sono eseguibili in Junit e ignorano l’intero problema pubblico / non pubblico. Nota a margine, è stato ufficialmente classificato come un “bug”, ma quando hanno provato a ripararlo, è stato sollevato un tale temporale che è stato rimesso come era originariamente.

Sono assolutamente d’accordo con @duffymo che il codice dovrebbe essere testato dal punto di vista del cliente (anche se dice che ha smesso di pensare in questo modo). Tuttavia, da questo punto di vista, privato vs altri hanno significati diversi. Il client di un metodo privato è la class stessa, quindi preferisco testarli tramite l’API esterna (pubblica / protetta dai pacchetti). Tuttavia, i membri protetti e protetti dal pacchetto sono disponibili per i client esterni, quindi li collaudo con i falsi che ereditano la class proprietaria o che risiedono nello stesso pacchetto.