Sto lavorando su un progetto java. Sono nuovo al test unitario. Qual è il modo migliore per testare i metodi privati nelle classi java?
Sto lavorando su un progetto java. Sono nuovo al test unitario. Qual è il modo migliore per testare i metodi privati nelle classi java?
Generalmente non collaudi direttamente i metodi privati. Dal momento che sono privati, considerali come dettagli di implementazione. Nessuno chiamerà mai uno di loro e si aspetta che funzioni in un modo particolare.
Dovresti testare la tua interfaccia pubblica. Se i metodi che chiamano i tuoi metodi privati funzionano come ti aspetti, supponi per estensione che i tuoi metodi privati funzionino correttamente.
In generale, lo eviterei. Se il tuo metodo privato è così complesso da richiedere un test unitario separato, significa spesso che merita la sua classe. Questo potrebbe incoraggiarti a scriverlo in un modo che sia riutilizzabile. Dovresti quindi testare la nuova classe e chiamare la sua interfaccia pubblica nella tua vecchia classe.
D'altra parte, a volte, il fatto di includere i dettagli di implementazione in classi separate porta a classi con interfacce complesse, molti passaggi di dati tra la vecchia e la nuova classe, o ad un design che può sembrare buono dal punto di vista OOP, ma non corrisponde alle intuizioni provenienti dal dominio del problema (ad esempio, dividere un modello di prezzo in due parti solo per evitare di testare i metodi privati non è molto intuitivo e può portare a problemi in seguito quando si mantiene / si estende il codice). Non vuoi avere "classi gemelle" che sono sempre cambiate insieme.
Di fronte a una scelta tra incapsulamento e testabilità, preferirei optare per il secondo. È più importante avere il codice corretto (cioè produrre l'output corretto) rispetto a un bel progetto OOP che non funziona correttamente, perché non è stato testato in modo adeguato. In Java, puoi semplicemente dare l'accesso al metodo "predefinito" e inserire il test dell'unità nello stesso pacchetto. I test unitari sono semplicemente parte del pacchetto che stai sviluppando, ed è OK avere una dipendenza tra i test e il codice che viene testato. Significa che quando si modifica l'implementazione, potrebbe essere necessario cambiare i test, ma va bene - ogni modifica dell'implementazione richiede di ripetere il test del codice, e se i test devono essere modificati per farlo, basta fare esso.
In generale, una classe può offrire più di un'interfaccia. C'è un'interfaccia per gli utenti e un'interfaccia per i manutentori. Il secondo può esporre di più per garantire che il codice sia adeguatamente testato. Non deve essere un test unitario su un metodo privato - potrebbe essere, ad esempio, la registrazione. Anche la registrazione "interrompe l'incapsulamento", ma lo facciamo ancora, perché è molto utile.
Il test dei metodi privati dipenderebbe dalla loro complessità; alcuni metodi privati di una linea non giustificherebbero lo sforzo extra di test (ciò si può dire anche dei metodi pubblici), ma alcuni metodi privati possono essere altrettanto complessi dei metodi pubblici e difficili da testare attraverso l'interfaccia pubblica. p>
La mia tecnica preferita è rendere privato il pacchetto del metodo privato, che consentirà l'accesso a un test di unità nello stesso pacchetto ma sarà comunque incapsulato da tutto il resto del codice. Ciò darà il vantaggio di testare direttamente la logica del metodo privato invece di dover fare affidamento su un test del metodo pubblico per coprire tutte le parti della (eventualmente) logica complessa.
Se questo è accoppiato con l'annotazione @Visiblestrongsting nella libreria Google Guava, stai chiaramente contrassegnando questo metodo privato del pacchetto come visibile solo per i test e, come tale, non dovrebbe essere chiamato da nessun'altra classe.
Gli oppositori di questa tecnica sostengono che ciò interromperà l'incapsulamento e aprirà metodi privati per codificare nello stesso pacchetto. Mentre sono d'accordo che questo rompe l'incapsulamento e apre il codice privato ad altre classi, sostengo che testare la logica complessa è più importante dell'incapsulamento strict e non usare i metodi privati del pacchetto che sono chiaramente contrassegnati come visibile solo per i test deve essere responsabilità degli sviluppatori utilizzare e modificare la base di codice.
Metodo privato prima del test:
private int add(int a, int b){
return a + b;
}
Il pacchetto metodo privato pronto per il test:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Nota: Mettere i test nello stesso pacchetto non equivale a metterli nella stessa cartella fisica. Separare il codice principale e il codice di test in strutture di cartelle fisiche separate è una buona pratica in generale, ma questa tecnica funzionerà finché le classi saranno definite nello stesso pacchetto.
Se non puoi utilizzare API esterne o non vuoi, puoi comunque utilizzare l'API JDK standard pura per accedere ai metodi privati utilizzando la reflection. Ecco un esempio
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Controlla tutorial Java link o API Java link per ulteriori informazioni.
Come @kij ha citato sulla sua risposta, ci sono momenti in cui una semplice soluzione che usa il reflection è davvero buona per testare un metodo privato.
Caso di test unitario significa testare l'unità di codice. Non significa testare l'interfaccia perché se stai testando l'interfaccia, questo non significa che stai testando l'unità di codice. Diventa una sorta di test della scatola nera. Inoltre, è meglio trovare problemi sul livello di unità più piccolo rispetto alla determinazione dei problemi a livello di interfaccia e quindi provare a eseguire il debug di quel pezzo che non funzionava. Pertanto, il test case dovrebbe essere testato indipendentemente dal loro ambito. Di seguito è riportato un modo per testare i metodi privati.
Se si utilizza java, è possibile utilizzare jmockit che fornisce Deencapsulation.invoke per chiamare qualsiasi metodo privato della classe in fase di test. Usa la riflessione per chiamarlo alla fine ma fornisce un bel wrapper attorno ad esso. ( link )
Prima di tutto, come suggerito da altri autori: pensaci due volte se hai davvero bisogno di testare il metodo privato. E se sì, ...
In .NET puoi convertirlo in metodo "Interno" e creare il pacchetto " InternalVisible " al progetto di test dell'unità.
In Java è possibile scrivere test nella classe da testare e i metodi di test dovrebbero essere in grado di chiamare anche metodi privati. Non ho una grande esperienza di Java, quindi probabilmente non è la migliore pratica.
Grazie.
Solitamente sono protetto da tali metodi. Supponiamo che la tua lezione sia in:
src/main/java/you/Class.java
È possibile creare una classe di test come:
src/test/java/you/TestClass.java
Ora hai accesso ai metodi protetti e puoi testarli unitamente (JUnit o TestNG non contano davvero), ma tieni questi metodi dai chiamanti che non volevi.
Si noti che ciò si aspetta un albero dei sorgenti di tipo Maven.
Se hai davvero bisogno di testare un metodo privato, con Java intendo, puoi usare fest assert
e / o fest reflect
. Usa la riflessione.
Importa la libreria con Maven (le versioni fornite non sono l'ultima cosa che penso) o importa direttamente nel tuo classpath:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
Ad esempio, se hai una classe denominata "MyClass" con un metodo privato chiamato "myPrivateMethod" che accetta un parametro String come un aggiornamento il suo valore a "this is cool test!", puoi eseguire il seguente test di junit :
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
Questa libreria ti consente anche di sostituire qualsiasi proprietà del bean (indipendentemente dal fatto che siano private e che nessun setter sia scritto) da un mock, e l'utilizzo di questo con Mockito o qualsiasi altro mock framework è davvero interessante. L'unica cosa che devi sapere al momento (non so se sarà meglio nelle prossime versioni) è il nome del campo / metodo target che vuoi manipolare e la sua firma.
Quello che faccio normalmente in C # è rendere i miei metodi protetti e non privati. È un modificatore di accesso un po 'meno privato, ma nasconde il metodo da tutte le classi che non ereditano dalla classe sotto test.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Qualsiasi classe che non eredita direttamente da classUnderTest non ha idea che methodToTest esista anche. Nel mio codice di test, posso creare una speciale classe di test che estende e fornisce l'accesso a questo metodo ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Questa classe esiste solo nel mio progetto di test. Il suo unico scopo è fornire accesso a questo singolo metodo. Mi consente di accedere a luoghi che la maggior parte delle altre classi non hanno.
Puoi testare facilmente i metodi privati se metti i tuoi test unitari in una classe interiore sulla classe che stai testando. Usando TestNG i tuoi test di unità devono essere classi interne statiche pubbliche annotate con @Test, come questo:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Poiché si tratta di una classe interna, è possibile chiamare il metodo privato.
I miei test vengono eseguiti da Maven e trova automaticamente questi casi di test. Se vuoi solo testare una classe, puoi fare
$ mvn test -Dtest=*UserEditorTest
Fonte: link
Leggi altre domande sui tag unit-testing java