it-swarm.dev

Wie testest du private Methoden?

Ich arbeite an einem Java Projekt. Ich bin neu im Unit-Test. Was ist der beste Weg, um private Methoden in Java Klassen) zu testen?

212

Im Allgemeinen testen Sie private Methoden nicht direkt. Betrachten Sie sie als Implementierungsdetail, da sie privat sind. Niemand wird jemals einen von ihnen anrufen und erwarten, dass es auf eine bestimmte Weise funktioniert.

Sie sollten stattdessen Ihre öffentliche Schnittstelle testen. Wenn die Methoden, die Ihre privaten Methoden aufrufen, wie erwartet funktionieren, gehen Sie im weiteren Sinne davon aus, dass Ihre privaten Methoden ordnungsgemäß funktionieren.

257
Adam Lear

Im Allgemeinen würde ich es vermeiden. Wenn Ihre private Methode so komplex ist, dass ein separater Komponententest erforderlich ist, bedeutet dies häufig, dass sie eine eigene Klasse verdient hat. Dies kann Sie dazu ermutigen, es auf eine Weise zu schreiben, die wiederverwendbar ist. Sie sollten dann die neue Klasse testen und die öffentliche Schnittstelle in Ihrer alten Klasse aufrufen.

Andererseits führt das Ausklammern der Implementierungsdetails in separate Klassen manchmal zu Klassen mit komplexen Schnittstellen, vielen Daten, die zwischen der alten und der neuen Klasse übertragen werden, oder zu einem Design, das vom Standpunkt OOP aus gut aussehen kann Dies entspricht jedoch nicht den Intuitionen aus dem Problembereich (z. B. ist die Aufteilung eines Preismodells in zwei Teile, um das Testen privater Methoden zu vermeiden, nicht sehr intuitiv und kann später zu Problemen bei der Pflege/Erweiterung des Codes führen). Sie möchten keine "Zwillingsklassen" haben, die immer zusammen geändert werden.

Wenn ich vor der Wahl zwischen Kapselung und Testbarkeit stehe, würde ich lieber die zweite wählen. Es ist wichtiger, den richtigen Code zu haben (d. H. Die richtige Ausgabe zu erzeugen) als ein schönes OOP Design, das nicht richtig funktioniert, weil es nicht ausreichend getestet wurde. In Java können Sie einfach der Methode "Standard" -Zugriff gewähren und den Komponententest in dasselbe Paket einfügen. Unit-Tests sind einfach Teil des Pakets, das Sie entwickeln, und es ist in Ordnung, eine Abhängigkeit zwischen den Tests und dem zu testenden Code zu haben. Wenn Sie die Implementierung ändern, müssen Sie möglicherweise Ihre Tests ändern, aber das ist in Ordnung. Bei jeder Änderung der Implementierung muss der Code erneut getestet werden. Wenn die Tests dazu geändert werden müssen, tun Sie dies einfach es.

Im Allgemeinen kann eine Klasse mehr als eine Schnittstelle anbieten. Es gibt eine Schnittstelle für die Benutzer und eine Schnittstelle für die Betreuer. Der zweite kann mehr verfügbar machen, um sicherzustellen, dass der Code angemessen getestet wird. Es muss kein Komponententest für eine private Methode sein - es kann sich beispielsweise um eine Protokollierung handeln. Die Protokollierung "bricht auch die Kapselung", aber wir tun es trotzdem, weil es so nützlich ist.

122
quant_dev

Das Testen privater Methoden würde von ihrer Komplexität abhängen. Einige einzeilige private Methoden würden den zusätzlichen Aufwand für das Testen nicht wirklich rechtfertigen (dies gilt auch für öffentliche Methoden), aber einige private Methoden können genauso komplex sein wie öffentliche Methoden und über die öffentliche Schnittstelle schwer zu testen sein.

Meine bevorzugte Technik besteht darin, das private Methodenpaket privat zu machen, wodurch der Zugriff auf einen Komponententest im selben Paket ermöglicht wird, der jedoch weiterhin aus allen anderen Codes gekapselt wird. Dies bietet den Vorteil, dass die Logik der privaten Methode direkt getestet werden kann, anstatt sich auf einen öffentlichen Methodentest verlassen zu müssen, um alle Teile der (möglicherweise) komplexen Logik abzudecken.

Wenn dies mit der Annotation @VisibleForTesting in der Google Guava-Bibliothek gekoppelt ist, markieren Sie diese private Paketmethode eindeutig als nur zum Testen sichtbar und sollten daher von keiner anderen Klasse aufgerufen werden.

Gegner dieser Technik argumentieren, dass dies die Kapselung unterbricht und private Methoden zum Codieren im selben Paket öffnet. Obwohl ich damit einverstanden bin, dass dies die Kapselung unterbricht und privaten Code für andere Klassen öffnet, argumentiere ich, dass das Testen komplexer Logik wichtiger ist als die strikte Kapselung und das Nichtverwenden von Paketen Private Methoden, die deutlich als nur zum Testen sichtbar markiert sind, müssen in der Verantwortung der Entwickler liegen, die die Codebasis verwenden und ändern.

Private Methode vor dem Testen:

private int add(int a, int b){
    return a + b;
}

Paket private Methode zum Testen bereit:

@VisibleForTesting
int add(int a, int b){
    return a + b;
}

Hinweis: Das Einfügen von Tests in dasselbe Paket entspricht nicht dem Einfügen von Tests in denselben physischen Ordner. Das Aufteilen Ihres Hauptcodes und Testcodes in separate physische Ordnerstrukturen ist im Allgemeinen eine gute Vorgehensweise. Diese Technik funktioniert jedoch, solange die Klassen wie im selben Paket definiert sind.

33
Richard

Wenn Sie keine externen APIs verwenden können oder nicht möchten, können Sie weiterhin die reine Standard-JDK-API verwenden, um mithilfe von Reflection auf private Methoden zuzugreifen. Hier ist ein Beispiel

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);

Überprüfen Sie Java Tutorial http://docs.Oracle.com/javase/tutorial/reflect/ oder Java API http://docs.Oracle.com/javase/7/docs/api/Java/lang/reflect/package-summary.html für weitere Informationen.

Wie @kij in seiner Antwort zitierte, gibt es Zeiten, in denen eine einfache Lösung mit Reflexion wirklich gut ist, um eine private Methode zu testen.

15
Gustavo Coelho

Unit Test Case bedeutet das Testen der Codeeinheit. Dies bedeutet nicht, dass Sie die Schnittstelle testen. Wenn Sie die Schnittstelle testen, bedeutet dies nicht, dass Sie die Codeeinheit testen. Es wird zu einer Art Black-Box-Test. Außerdem ist es besser, Probleme auf der Ebene der kleinsten Einheiten zu finden, als die Probleme auf der Schnittstellenebene zu ermitteln und dann zu versuchen, das zu debuggen, welches Teil nicht funktioniert hat. Daher sollte der Unit-Testfall unabhängig von seinem Umfang getestet werden. Im Folgenden finden Sie eine Möglichkeit, private Methoden zu testen.

Wenn Sie Java verwenden, können Sie jmockit verwenden, das Deencapsulation.invoke bereitstellt, um eine beliebige private Methode der zu testenden Klasse aufzurufen. Es verwendet Reflektion, um es schließlich aufzurufen, bietet aber einen Nice-Wrapper darum. ( https://code.google.com/p/jmockit/ )

9
agrawalankur

Zunächst einmal, wie andere Autoren vorgeschlagen haben: Überlegen Sie zweimal, ob Sie die private Methode wirklich testen müssen. Und wenn, ...

In .NET können Sie es in die Methode "Intern" konvertieren und das Paket " InternalVisible " zu Ihrem Unit-Test-Projekt machen.

In Java können Sie Tests selbst in die zu testende Klasse schreiben und Ihre Testmethoden sollten auch private Methoden aufrufen können. Ich habe nicht wirklich große Java Erfahrung, das ist also wahrscheinlich nicht die beste Vorgehensweise.

Vielen Dank.

8
Budda

Normalerweise mache ich solche Methoden geschützt. Angenommen, Ihre Klasse ist in:

src/main/Java/you/Class.Java

Sie können eine Testklasse erstellen als:

src/test/Java/you/TestClass.Java

Jetzt haben Sie Zugriff auf geschützte Methoden und können diese Unit-Tests durchführen (JUnit oder TestNG spielen keine Rolle). Sie halten diese Methoden jedoch von Anrufern fern, die Sie nicht wollten.

Beachten Sie, dass dies einen Quellbaum im Maven-Stil erwartet.

8
gyorgyabraham

Wenn Sie wirklich eine private Methode testen müssen, können Sie mit Java Ich meine, Sie können fest assert Und/oder fest reflect Verwenden. Sie verwendet Reflektion.

Importieren Sie die Bibliothek mit maven (bestimmte Versionen sind meiner Meinung nach nicht die neuesten) oder importieren Sie sie direkt in Ihren Klassenpfad:

<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>

Wenn Sie beispielsweise eine Klasse mit dem Namen 'MyClass' mit einer privaten Methode namens 'myPrivateMethod' haben, die einen String als Parameter verwendet und dessen Wert auf 'this is cool testing!' Aktualisiert, können Sie den folgenden Junit-Test durchführen:

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);
}

Mit dieser Bibliothek können Sie auch alle Bean-Eigenschaften (unabhängig davon, ob sie privat sind oder keine Setter geschrieben sind) durch ein Mock ersetzen. Die Verwendung dieser Eigenschaft mit Mockito oder einem anderen Mock-Framework ist wirklich cool. Das einzige, was Sie im Moment wissen müssen (ich weiß nicht, ob dies in den nächsten Versionen besser sein wird), ist der Name des Zielfelds/der Zielmethode, die Sie bearbeiten möchten, und seine Signatur.

3
kij

Normalerweise mache ich meine Methoden in C # geschützt und nicht privat. Es ist ein etwas weniger privater Zugriffsmodifikator, aber es verbirgt die Methode vor allen Klassen, die nicht von der zu testenden Klasse erben.

public class classUnderTest
{
   //this would normally be private
   protected void methodToTest()
   {
     //some code here
   }
}

Jede Klasse, die nicht direkt von classUnderTest erbt, hat keine Ahnung, dass methodToTest überhaupt existiert. In meinem Testcode kann ich eine spezielle Testklasse erstellen, die diese Methode erweitert und Zugriff darauf bietet ...

class TestingClass : classUnderTest
{
   public void methodToTest()
   {
     //this returns the method i would like to test
     return base.methodToTest();
   }
}

Diese Klasse existiert nur in meinem Testprojekt. Ihr einziger Zweck besteht darin, den Zugang zu dieser einzelnen Methode zu ermöglichen. Es ermöglicht mir den Zugriff auf Orte, die die meisten anderen Klassen nicht haben.

2
spaceman

Sie können private Methoden einfach testen, wenn Sie Ihre Komponententests in einer inneren Klasse für die Klasse platzieren, die Sie testen. Bei Verwendung von TestNG müssen Ihre Komponententests öffentliche statische innere Klassen sein, die mit @Test wie folgt kommentiert sind:

@Test
public static class UserEditorTest {
    public void test_private_method() {
       assertEquals(new UserEditor().somePrivateMethod(), "success");
    }
}

Da es sich um eine innere Klasse handelt, kann die private Methode aufgerufen werden.

Meine Tests werden von maven ausgeführt und finden diese Testfälle automatisch. Wenn Sie nur eine Klasse testen möchten, können Sie dies tun

$ mvn test -Dtest=*UserEditorTest

Quelle: http://www.ninthavenue.com.au/how-to-unit-test-private-methods

1
Roger Keays