3 miejsca gdzie testy sprawdzają się najlepiej

3 miejsca gdzie testy jednostkowe sprawdzają się najlepiej

Testy jednostkowe sprawdzają się dobrze w zasadzie wszędzie tam, gdzie trzeba coś przetestować. Czyli może to być nawet najdrobniejsza metoda, która zawiera prostego ifa. Są jednak sytuacje, gdzie użycie ich daje bardzo dużo korzyści. I tam powinieneś stosować je zawsze.

1. Algorytmy

Testowanie różnego rodzaju algorytmów często bywa trudne i długotrwałe. Wyobraź sobie, że z każdą drobną zmianą algorytmu odpalasz serwer aplikacyjny, przechodzisz do przeglądarki, wyklikujesz odpowiednią podstronę i sprawdzasz czy zmiany, które właśnie wprowadziłeś dają dobry efekt. I tak 120 razy. To strasznie czasochłonne i męczące, ale da się to tak zrobić. No i jak już to zrobię w taki sposób, to przecież testy nie będą mi już potrzebne. Tak? No nie. Algorytmy mają to do siebie, że ich implementacja może się zmieniać. A czasem nawet powinna np. ze względów wydajnościowych.

Gdy chcemy zmienić implementację algorytmu musimy przejść ten proces jeszcze raz. Tylko zauważ, że nie mamy żadnej pewności, że po zmianach algorytm będzie działał w taki sam sposób, jak przed zmianami. Tak, można spróbować przetestować to ręcznie i poprawić ewentualne błędy, ale w zależności od złożoności algorytmu może to być nie wystarczające. A jeśli implementacja algorytmu będzie zmieniać się kilka razy to narażasz się na uszkodzenie tej samej funkcjonalności kilka razy. Jak myślisz, jak zareagują użytkownicy twojej aplikacji, gdy zobaczą, że jedna część aplikacji raz działa, a raz nie?

Jeśli za pierwszym razem napiszesz odpowiednio dobrze testy, to nie dość, że pomogą Ci one napisać prawidłowo algorytm, to pomogą Ci także przy kolejnych zmianach danego algorytmu. Poza tym oszczędzisz masę czasu na przeklikiwanie się przez aplikację i frustrację twoich użytkowników.

 

Dla przykładu rozpatrzmy bardzo popularny algorytm wyliczania ciągu Fibonacciego. Ciąg ten można zaimplemntować w Javie co najmniej na trzy sposoby: iteracyjnie, rekurencyjnie i przy użyciu streamów (co w sumie też jest iteracją).

public class Fibonacci {

  public static int[] fibonacci(int n) {
    int t1 = 0;
    int t2 = 1;
    int[] results = new int[n+1];
    results[0] = t1;
    for(int i = 1; i <= n; i++) {
      int sum = t1 + t2;
      t1 = t2;
      t2 = sum;
      results[i] = t1;
    }
    return results;
  }
}

Metoda zwraca tablicę ze wszystkimi wyrazami ciągu dla danej liczby n.

Test w tym przypadku jest bardzo prosty (i wystarczy nam tylko jeden), ale pozwoli on przetestować wszystkie implementacje jakie można stworzyć dla tego algorytmu.

import org.junit.Test;
import static org.junit.Assert.*;

public class FibonacciTest {

  @Test
  public void shouldReturnsFibonacciSeries() {
    int[] fibonacci = Fibonacci.fibonacci(7);
    assertArrayEquals(new int[]{0, 1, 1, 2, 3, 5, 8, 13}, fibonacci);
  }
}

Prawda, że proste?

 

2. Wyrażenia regularne

Kolejną rzeczą, którą zawsze warto testować przy pomocy testów jednostkowych to wyrażenia regularne. Nigdy nie byłem mistrzem wyrażeń regularnych i myślę, że niewielu programistów jest. Kiedyś stosowałem różnego rodzaju aplikacje, które pomagały mi „wizualnie” przetestować jakieś wyrażenie. Sprawdzało się to całkiem nieźle, ale było prawie tak samo mało wydajne jak przeklikiwanie się przez aplikację, żeby sprawdzić, czy wszystko działa tak jak trzeba.

I tutaj też świetnie sprawdzają się testy, a czasem nawet dobrym rozwiązaniem są testy sparametryzowane, które pozwalają przetestować dane wyrażenie, korzystając z listy parametrów.

import java.util.regex.Pattern;

public class Regex {

  private static Pattern pattern = Pattern.compile("[a-z]{5}");

  public static boolean checkRegexMacht(String string) {
    return pattern.matcher(string).matches();
  }
}

Oczywiście, to tylko prosty przykład demonstrujący testowanie regexów. Zwykle wyrażenia są bardziej skomplikowane, a testów więcej.

import org.junit.Test;
import static org.junit.Assert.*;

public class RegexTest {

  @Test
  public void regexShouldMatch() {
    boolean result = Regex.checkRegexMacht("asdas");
    assertTrue(result);
  }

  @Test
  public void regexShouldNotMatchShorterString() {
    boolean result = Regex.checkRegexMacht("aaa");
    assertFalse(result);
  }

  @Test
  public void regexShouldNotMatchLongerString() {
    boolean result = Regex.checkRegexMacht("aaaaaa");
    assertFalse(result);
  }
}

 

3. Metody z dużą złożonością

Metody takie zawierają bardzo dużą ilość ifów, a konstrukcja tych metod wynika z bardzo złożonego problemu biznesowego. Często są to też implementacje różnych biznesowych algorytmów.  Bardzo łatwo jest zrobić dużą ilość błędów w takiej metodzie. Podobnie jak w przypadku algorytmów i tutaj testowanie będzie bardzo czasochłonne i niedokładne. Oczywiście zawsze trzeba się zastanowić, czy ta metoda powinna tak wyglądać. Może drobny refaktoring pomoże zredukować złożoność takiej metody.

Tak czy inaczej, złożona logika powinna być dobrze przetestowana. Często zaleca się otestowanie takiej metody testami jednostkowymi i późniejszy refaktoring. Tutaj też pomogą nam testy.

Im więcej ifów, im więcej warunków w tych ifach, tym sytuacja bardziej się komplikuje. Ręczne przetestowanie wszystkich zależności, które powstają w takiej metodzie jest zwykle niemożliwe. Dlatego warto testować takie metody bardzo dokładnie.

Trudno jest tu podać jakiś reprezentatywny przykład, więc na razie go nie będzie. Jest szansa, że znajdę coś ciekawego na githubie, a jeśli Ty masz jakiś ciekawy przykład, to podeślij w komentarzach. Chętnie mu się przyjrzę.

 

Podsumowanie

Testy warto stosować wszędzie, gdzie jest coś do przetestowania. Nie tylko w tych trzech wymienionych przypadkach. Zawsze najwięcej zyskasz, gdy będziesz je stosował tam, gdzie kod jest w jakiś sposób skomplikowany. Testując manualnie, nie testujesz zawsze wszystkich możliwych ścieżek. Zwykle testujesz tylko to, co zmieniasz, albo kilka (2-3) najważniejszych ścieżek. Trudno jest za każdym razem testować manualnie wszystkie możliwe ścieżki.

Testami jednostkowymi jesteś w stanie pokryć wszystkie lub prawie wszystkie ścieżki, które powstają w twoim oprogramowaniu. Z każdą zmianą jesteś w stanie odpalić wszystkie dostępne testy i przetestować aplikację w dużo większym stopniu niż zrobiłbyś to manualnie.

 

Po co mi testy jednostkowe?

 

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<