Pracuję nad projektem Java. Jestem nowy w testowaniu jednostkowym. Jaki jest najlepszy sposób na testowanie jednostkowe metod prywatnych w klasach Java?
Pracuję nad projektem Java. Jestem nowy w testowaniu jednostkowym. Jaki jest najlepszy sposób na testowanie jednostkowe metod prywatnych w klasach Java?
Odpowiedzi:
Zasadniczo nie testuje się bezpośrednio metod prywatnych. Ponieważ są one prywatne, rozważ je jako szczegół implementacji. Nikt nigdy nie zadzwoni do jednego z nich i nie spodziewa się, że zadziała w określony sposób.
Zamiast tego powinieneś przetestować swój publiczny interfejs. Jeśli metody wywołujące metody prywatne działają zgodnie z oczekiwaniami, wówczas przyjmujemy przez rozszerzenie, że metody prywatne działają poprawnie.
Ogólnie bym tego unikał. Jeśli Twoja prywatna metoda jest tak złożona, że wymaga oddzielnego testu jednostkowego, często oznacza to, że zasłużyła na swoją klasę. Może to zachęcić Cię do napisania go w sposób, który można ponownie wykorzystać. Następnie powinieneś przetestować nową klasę i wywołać jej publiczny interfejs w starej klasie.
Z drugiej strony czasami dzielenie szczegółów implementacji na osobne klasy prowadzi do klas o złożonych interfejsach, dużej ilości danych przesyłanych między starą i nową klasą lub do projektu, który może wyglądać dobrze z punktu widzenia OOP, ale nie dopasować intuicje pochodzące z dziedziny problemowej (np. podzielenie modelu wyceny na dwie części, aby uniknąć testowania metod prywatnych, nie jest zbyt intuicyjne i może prowadzić do problemów w późniejszym czasie podczas utrzymywania / rozszerzania kodu). Nie chcesz mieć „klas bliźniaków”, które zawsze są zmieniane razem.
W obliczu wyboru między enkapsulacją a testowalnością wolałbym drugi. Ważniejsze jest, aby mieć poprawny kod (tj. Wygenerować poprawny wynik) niż ładny projekt OOP, który nie działa poprawnie, ponieważ nie został odpowiednio przetestowany. W Javie możesz po prostu dać dostęp do metody „domyślnej” i umieścić test jednostkowy w tym samym pakiecie. Testy jednostkowe są po prostu częścią pakietu, który opracowujesz, i dobrze jest mieć zależność między testami a testowanym kodem. Oznacza to, że po zmianie implementacji może być konieczna zmiana testów, ale to jest OK - każda zmiana implementacji wymaga ponownego przetestowania kodu, a jeśli testy muszą zostać zmodyfikowane, aby to zrobić, wystarczy to zrobić to.
Ogólnie klasa może oferować więcej niż jeden interfejs. Istnieje interfejs dla użytkowników i interfejs dla opiekunów. Drugi może ujawnić więcej, aby zapewnić odpowiednie przetestowanie kodu. Nie musi to być test jednostkowy metodą prywatną - może to być na przykład logowanie. Rejestrowanie również „przerywa enkapsulację”, ale wciąż to robimy, ponieważ jest to bardzo przydatne.
Testowanie metod prywatnych zależałoby od ich złożoności; niektóre prywatne metody jednowierszowe nie wymagałyby dodatkowego wysiłku związanego z testowaniem (można to również powiedzieć o metodach publicznych), ale niektóre prywatne metody mogą być tak samo złożone jak metody publiczne i trudne do przetestowania przez interfejs publiczny.
Moją preferowaną techniką jest uczynienie prywatnego pakietu metod prywatnym, który pozwoli na dostęp do testu jednostkowego w tym samym pakiecie, ale nadal będzie enkapsulowany z całego innego kodu. Daje to zaletę bezpośredniego testowania logiki metody prywatnej zamiast konieczności polegania na publicznym metodzie testu obejmującej wszystkie części (ewentualnie) złożonej logiki.
Jeśli jest to sparowane z adnotacją @VisibleForTesting w bibliotece Google Guava, wyraźnie zaznaczasz ten pakiet jako metodę prywatną jako widoczną tylko do testowania i jako taki nie powinien być wywoływany przez inne klasy.
Przeciwnicy tej techniki twierdzą, że przerwie to enkapsulację i otworzy prywatne metody kodowania w tym samym pakiecie. Chociaż zgadzam się, że przerywa to enkapsulację i otwiera prywatny kod dla innych klas, argumentuję, że testowanie złożonej logiki jest ważniejsze niż ścisłe enkapsulowanie i nie używanie prywatnych metod pakietu, które są wyraźnie oznaczone jako widoczne do testowania, musi być obowiązkiem programistów używając i zmieniając bazę kodu.
Metoda prywatna przed testowaniem:
private int add(int a, int b){
return a + b;
}
Pakiet prywatnej metody gotowy do testowania:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Uwaga: Umieszczenie testów w tym samym pakiecie nie jest równoważne z umieszczeniem ich w tym samym folderze fizycznym. Rozdzielanie kodu głównego i kodu testowego na osobne struktury folderów fizycznych jest ogólnie dobrą praktyką, ale ta technika będzie działać, dopóki klasy będą zdefiniowane jak w tym samym pakiecie.
Jeśli nie możesz używać zewnętrznych interfejsów API lub nie chcesz, możesz nadal używać czystego standardowego interfejsu API JDK, aby uzyskać dostęp do prywatnych metod za pomocą refleksji. Oto przykład
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);
Sprawdź samouczek Java http://docs.oracle.com/javase/tutorial/reflect/ lub Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary. HTML, aby uzyskać więcej informacji.
Jak cytował @kij w swojej odpowiedzi, zdarza się, że proste rozwiązanie wykorzystujące odbicie jest naprawdę dobre do przetestowania prywatnej metody.
Przypadek testowy jednostki oznacza testowanie jednostki kodu. Nie oznacza to testowania interfejsu, ponieważ jeśli testujesz interfejs, nie oznacza to, że testujesz jednostkę kodu. To staje się rodzajem testowania czarnej skrzynki. Ponadto lepiej jest znaleźć problemy na najmniejszym poziomie jednostki niż określić problemy na poziomie interfejsu, a następnie próbować debugować, który element nie działał. Dlatego przypadek testowy powinien być testowany niezależnie od jego zakresu. Poniżej przedstawiono sposób przetestowania metod prywatnych.
Jeśli używasz java, możesz użyć jmockit, który zapewnia Deencapsulation.invoke, aby wywołać dowolną prywatną metodę testowanej klasy. Używa refleksji, aby w końcu ją nazwać, ale zapewnia ładne otoczenie. ( https://code.google.com/p/jmockit/ )
Po pierwsze, jak sugerowali inni autorzy: zastanów się dwa razy, jeśli naprawdę potrzebujesz przetestować metodę prywatną. A jeśli tak, ...
W .NET możesz przekonwertować go na metodę „ Wewnętrzną ” i ustawić pakiet „ InternalVisible ” na projekt testu jednostkowego.
W Javie możesz pisać testy w testowanej klasie, a twoje metody testowe powinny mieć możliwość wywoływania metod prywatnych. Tak naprawdę nie mam dużego doświadczenia z Javą, więc prawdopodobnie nie jest to najlepsza praktyka.
Dzięki.
Zazwyczaj chronię takie metody. Powiedzmy, że Twoja klasa jest w:
src/main/java/you/Class.java
Możesz utworzyć klasę testową jako:
src/test/java/you/TestClass.java
Teraz masz dostęp do chronionych metod i możesz je testować jednostkowo (JUnit lub TestNG nie ma tak naprawdę znaczenia), ale ukrywasz te metody przed dzwoniącymi, których nie chciałeś.
Zauważ, że oczekuje to drzewa źródłowego w stylu raju.
Jeśli naprawdę potrzebujesz przetestować metodę prywatną, z Javą mam na myśli, możesz użyć fest assert
i / lub fest reflect
. Wykorzystuje odbicie.
Zaimportuj bibliotekę za pomocą maven (dane wersje nie są chyba ostatnimi) lub zaimportuj ją bezpośrednio do ścieżki klasy:
<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>
Na przykład, jeśli masz klasę o nazwie „MyClass” z prywatną metodą o nazwie „myPrivateMethod”, która przyjmuje parametr String jako parametr i aktualizuje jego wartość do „to jest fajne testowanie!”, Możesz wykonać następujący test 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);
}
Ta biblioteka pozwala również zastąpić dowolne właściwości komponentu bean (bez względu na to, czy są prywatne i nie są zapisywane żadne ustawiacze) przez próbkę, a używanie tego z Mockito lub jakimkolwiek innym frameworkiem jest naprawdę fajne. Jedyne, co musisz wiedzieć w tej chwili (nie wiem, czy będzie lepiej w następnych wersjach), to nazwa pola / metody docelowej, którą chcesz manipulować, i jej podpis.
To, co zwykle robię w języku C #, sprawia, że moje metody są chronione, a nie prywatne. Jest to nieco mniej prywatny modyfikator dostępu, ale ukrywa metodę przed wszystkimi klasami, które nie dziedziczą po testowanej klasie.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Każda klasa, która nie dziedziczy bezpośrednio z classUnderTest, nie ma pojęcia, że methodToTest w ogóle istnieje. W moim kodzie testowym mogę utworzyć specjalną klasę testową, która rozszerza i zapewnia dostęp do tej metody ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Ta klasa istnieje tylko w moim projekcie testowym. Jego jedynym celem jest zapewnienie dostępu do tej jednej metody. Pozwala mi na dostęp do miejsc, których nie ma większość innych klas.
protected
jest częścią publicznego interfejsu API i podlega tym samym ograniczeniom (nigdy nie można go zmienić, musi zostać udokumentowany ...). W C # możesz używać internal
razem z InternalsVisibleTo
.
Możesz łatwo przetestować metody prywatne, jeśli umieścisz testy jednostkowe w klasie wewnętrznej w klasie, którą testujesz. Za pomocą TestNG testy jednostkowe muszą być publicznymi statycznymi klasami wewnętrznymi opatrzonymi adnotacją @Test, jak poniżej:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Ponieważ jest to klasa wewnętrzna, można wywołać metodę prywatną.
Moje testy są uruchamiane z maven i automatycznie wyszukuje te przypadki testowe. Jeśli chcesz tylko przetestować jedną klasę, możesz to zrobić
$ mvn test -Dtest=*UserEditorTest
Źródło: http://www.ninthavenue.com.au/how-to-unit-test-private-methods