Testy jednostkowe pełnią dwie bardzo ważne role:
- poprawiają design aplikacji
- sprawdzają czy kod jest poprawny
Drugi aspekt można rozumieć dwojako... Spójrzmy na przykładową metodę kalkulatora:
1: public class Calculator
2: {
3: public int Add(int first, int second)
4: {
5: return first + second;
6: }
7:
8:
9: }
Jej przetestowanie nie powinno sprawiać nikomu problemu. Ale... czy na pewno?
Jestem przekonany, że jeszcze jakiś czas temu sam napisałbym test w taki sposób:
1: [Test]
2: public void Add_ReturnsSum()
3: {
4: int firstArgument = 2;
5: int secondArgument = 3;
6:
7: int result = new Calculator().Add(firstArgument, secondArgument);
8:
9: Assert.AreEqual(2 + 3, result);
10: }
Co nam testuje taki test? Ano to, czy kod w teście i metodzie testowanej jest taki sam. Bez sensu. Przy prostym dodawaniu wygląda to oczywiście banalnie i niewinnie, ale co w przypadku skomplikowanych algorytmów? Idąc tą drogą możemy bardzo łatwo wpaść w pułapkę kopiuj/wklej polegającą na bezmyślnym kodowaniu dwukrotnie tego samego – przyznam się, że ja kiedyś wpadłem. Czy test, który właśnie napisaliśmy, sprawdza poprawność wyniku zwracanego przez metodę Add()? Nie, on sprawdza czy wykonywane są te same operacje. Test ten powinien wyglądać tak:
1: [Test]
2: public void Add_ReturnsSum()
3: {
4: int firstArgument = 2;
5: int secondArgument = 3;
6: int expectedResult = 5;
7:
8: int result = new Calculator().Add(firstArgument, secondArgument);
9:
10: Assert.AreEqual(expectedResult, result);
11: }
Czyli: dla danych argumentów metoda powinna zwrócić taki a nie inny wynik, obliczony przez nas niezależnie od samego programu. SPOSÓB obliczania tego wyniku nie powinien być przedmiotem badanym przez ten test jednostkowy.
Ciekawostka: na tej zasadzie działa PEX: generuje argumenty metody i zwracane przez nią wyniki, gdy jest wywołana z tymi argumentami.