Mateusz Wajnberger
TDD – Test Driven Development
Test Driven Development to technika wytwarzania oprogramowania, w której zanim napiszemy jakąś funkcjonalność najpierw tworzymy do niej test. W TDD mamy do czynienia z cyklami, które składają się z trzech faz: red, green, refactor. Pierwszym krokiem jest napisanie testu, który nie przechodzi. Następnie implementujemy funkcjonalność tak, aby dany test się udał przy czym na tym etapie nie przykładamy jeszcze dużej wagi do jakości kodu. Ostatnim etapem pojedynczego cyklu jest refaktoryzacja kodu jednak musimy być pewni, że nie zostanie zmieniona jego funkcjonalność. Może się zdarzyć, że faza refaktoryzacji nie zawsze jest konieczna. W celu zilustrowania omawianej metody posłużymy się grą „FizzBuzz”. Cała zabawa polega na tym, że jeżeli liczba naturalna jest podzielna przez trzy to zwracamy „Fizz”, jeżeli jest podzielna przez pięć to zwracamy „Buzz”, a jeżeli zarówno przez trzy i pięć to zwracamy „FizzBuzz”.
Tworzymy klasę „FizzBuzz” oraz klasę „FizzBussTest”, w której będziemy testować metodę „process”. Piszemy test, który nie przechodzi.
public class FizzBuzz {
public String process(int i) {
return String.valueOf(i);
}
}
public class FizzBuzzTest {
@Test
public void shouldProcessSingleNumber() {
FizzBuzz fizzBuzz = new FizzBuzz();
assertEquals("1", fizzBuzz.process(1));
assertEquals("2", fizzBuzz.process(2));
assertEquals("Fizz", fizzBuzz.process(3));
}
}
Implementujemy potrzebny kod, aby nasz test przeszedł.
public String process(int i) {
if(i == 3) {
return "Fizz";
}
return String.valueOf(i);
}
Rezygnujemy z refaktoryzacji działającego kodu więc przechodzimy do napisania kolejnej asercji, po której oczywiście nasz kod nie przechodzi. W tym momencie zaczyna się kolejny cykl.
@Test
public void shouldProcessSingleNumber() {
FizzBuzz fizzBuzz = new FizzBuzz();
assertEquals("1", fizzBuzz.process(1));
assertEquals("2", fizzBuzz.process(2));
assertEquals("Fizz", fizzBuzz.process(3));
assertEquals("Buzz", fizzBuzz.process(5));
}
Ponownie implementujemy potrzebny kod, aby nasz test przeszedł.
public String process(int i) {
if(i == 3) {
return "Fizz";
}
else if (i == 5) {
return "Buzz";
}
return String.valueOf(i);
}
Ponownie rezygnujemy z refaktoryzacji kodu, a co za tym idzie zaczynamy kolejny cykl, w którym sprawdzamy liczbę podzielną zarówno przez trzy jak i przez pięć. Piszemy kolejną asercję.
@Test
public void shouldProcessSingleNumber() {
FizzBuzz fizzBuzz = new FizzBuzz();
assertEquals("1", fizzBuzz.process(1));
assertEquals("2", fizzBuzz.process(2));
assertEquals("Fizz", fizzBuzz.process(3));
assertEquals("Buzz", fizzBuzz.process(5));
assertEquals("FizzBuzz", fizzBuzz.process(15));
}
Poprawiamy kod, aby dany test zakończył się powodzeniem.
public String process(int i) {
if((i % 3 == 0) && (i % 5 == 0)) {
return "FizzBuzz";
} else if(i % 3 == 0) {
return "Fizz";
} else if (i % 5 == 0) {
return "Buzz";
}
return String.valueOf(i);
}
Załóżmy, że w tym miejscu chcielibyśmy zrefaktoryzować nasz funkcjonujący kod ponieważ nie do końca podoba nam się litera „i” jako integer. Jest to trzeci etap poszczególnego cyklu. Na koniec sprawdzamy, czy refaktoryzacja nie wpłynęła negatywnie na funkcjonalność naszego kodu.
public String process(int number) {
if((number % 3 == 0) && (number % 5 == 0)) {
return "FizzBuzz";
} else if(number % 3 == 0) {
return "Fizz";
} else if (number % 5 == 0) {
return "Buzz";
}
return String.valueOf(number);
}