W świecie rzeczywistym pisanie testów jednostkowych dla kodu innej osoby jest całkowicie normalne. Jasne, pierwotny programista powinien już to zrobić, ale często otrzymujesz starszy kod, gdy tego po prostu nie zrobiono. Nawiasem mówiąc, nie ma znaczenia, czy ten starszy kod przyszedł kilkadziesiąt lat temu z odległej galaktyki, czy też jeden z twoich współpracowników sprawdził go w zeszłym tygodniu, czy też napisałeś go dzisiaj, starszy kod to kod bez testów
Zadaj sobie pytanie: dlaczego piszemy testy jednostkowe? Going Green jest oczywiście tylko środkiem do celu, ostatecznym celem jest udowodnienie lub obalenie twierdzeń dotyczących testowanego kodu.
Załóżmy, że masz metodę obliczającą pierwiastek kwadratowy z liczby zmiennoprzecinkowej. W Javie interfejs zdefiniowałby to jako:
public double squareRoot(double number);
Nie ma znaczenia, czy napisałeś implementację, czy też zrobił to ktoś inny, chcesz potwierdzić kilka właściwości squareRoot:
- że może zwrócić proste katalogi główne, takie jak sqrt (4.0)
- że może znaleźć prawdziwy root, jak sqrt (2.0) z rozsądną precyzją
- że stwierdzi, że sqrt (0,0) wynosi 0,0
- zgłasza IllegalArgumentException po podaniu liczby ujemnej, tj. na sqrt (-1.0)
Więc zacznij pisać jako indywidualne testy:
@Test
public void canFindSimpleRoot() {
assertEquals(2, squareRoot(4), epsilon);
}
Ups, ten test już się nie powiedzie:
java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers
Zapomniałeś o arytmetyki zmiennoprzecinkowej. OK, przedstaw się double epsilon=0.01
i idź:
@Test
public void canFindSimpleRootToEpsilonPrecision() {
assertEquals(2, squareRoot(4), epsilon);
}
i dodaj inne testy: w końcu
@Test
@ExpectedException(IllegalArgumentException.class)
public void throwsExceptionOnNegativeInput() {
assertEquals(-1, squareRoot(-1), epsilon);
}
i jeszcze raz:
java.lang.AssertionError: expected:<-1.0> but was:<NaN>
Powinieneś był przetestować:
@Test
public void returnsNaNOnNegativeInput() {
assertEquals(Double.NaN, squareRoot(-1), epsilon);
}
Co my tu zrobiliśmy? Zaczęliśmy od kilku założeń dotyczących tego, jak powinna zachowywać się metoda, i stwierdziliśmy, że nie wszystkie były prawdziwe. Następnie stworzyliśmy zestaw testowy Zielony, aby zapisać dowód, że metoda zachowuje się zgodnie z naszymi poprawionymi założeniami. Teraz klienci tego kodu mogą polegać na tym zachowaniu. Gdyby ktoś wymienił rzeczywistą implementację squareRoot na coś innego, coś, co na przykład naprawdę rzucił wyjątek zamiast zwracać NaN, nasze testy wychwyciłyby to natychmiast.
Ten przykład jest trywialny, ale często dziedziczysz duże fragmenty kodu, w których nie jest jasne, co właściwie robi. W takim przypadku normalne jest układanie uprzęży testowej wokół kodu. Zacznij od kilku podstawowych założeń dotyczących zachowania kodu, napisz dla nich testy jednostkowe, przetestuj. Jeśli zielony, dobrze, napisz więcej testów. Jeśli czerwony, to teraz masz nieudane stwierdzenie, które możesz trzymać przeciwko specyfikacji. Być może w starszym kodzie jest błąd. Być może specyfikacja nie jest jasna na temat tego konkretnego wejścia. Może nie masz specyfikacji. W takim przypadku przepisz test, aby udokumentował nieoczekiwane zachowanie:
@Test
public void throwsNoExceptionOnNegativeInput() {
assertNotNull(squareRoot(-1)); // Shouldn't this fail?
}
Z czasem powstaje uprząż testowa, która dokumentuje rzeczywisty kod i staje się swoistą zakodowaną specyfikacją. Jeśli kiedykolwiek chcesz zmienić starszy kod lub zastąpić go innym, masz uprząż testową, aby sprawdzić, czy nowy kod zachowuje się tak samo lub czy nowy kod zachowuje się inaczej w oczekiwany i kontrolowany sposób (na przykład, że w rzeczywistości naprawia błąd, którego usunięcia się spodziewasz). Ta uprząż nie musi być kompletna pierwszego dnia, w rzeczywistości posiadanie niepełnej uprzęży jest prawie zawsze lepsze niż brak uprzęży. Posiadanie uprzęży oznacza, że możesz łatwiej pisać kod klienta, wiesz, gdzie się spodziewać, że coś się zepsuje, gdy coś zmienisz, i gdzie się zepsuły, kiedy w końcu to zrobiły.
Powinieneś spróbować wyjść z myślenia, że musisz pisać testy jednostkowe tylko dlatego, że musisz, tak jakbyś wypełniał obowiązkowe pola w formularzu. I nie powinieneś pisać testów jednostkowych, aby czerwona linia była zielona. Testy jednostkowe nie są twoimi wrogami, testy jednostkowe są twoimi przyjaciółmi.