Es crea la primera prova, que comprova que la classe FizzBuzz existeix.
packageud4.tdd;importorg.junit.jupiter.api.Test;import staticorg.junit.jupiter.api.Assertions.assertEquals;classFizzBuzzTest{@Test@DisplayName("FizzBuzz class should exist")voidfizzBuzzClassShouldExist(){FizzBuzzfizzBuzz=newFizzBuzz();}}
Aquesta prova fallarà inicialment, ja que la classe FizzBuzz no existeix.
Es crea la segona prova, que comprova que existeix el mètode transform().
@Test@DisplayName("FizzBuzz::transform() method should exist")voidfizzBuzzTransformMethodShouldExist(){FizzBuzzfizzBuzz=newFizzBuzz();fizzBuzz.transform();}
Aquesta prova fallarà, ja que el mètode transform() no existeix.
Es crea la tercera prova, que comprova que el mètode transform() accepta un nombre enter
com a paràmetre.
@Test@DisplayName("FizzBuzz::transform() method should have an int parameter")voidfizzBuzzTransformMethodShouldHaveIntParameter(){FizzBuzzfizzBuzz=newFizzBuzz();fizzBuzz.transform(1);}
Per aconseguir que la prova passe, cal afegir el paràmetre int n al mètode transform().
publicclassFizzBuzz{publicvoidtransform(intn){}}
En aquest punt, si s'executen les proves, s'observa que la prova nova passa, però l'anterior falla,
perquè el mètode transform() ja no es pot invocar sense paràmetres. Per tant, cal decidir com procedir.
En aquest cas, com que la prova que falla és més antiga i comprova un comportament que també comprova
la prova actual, s'elimina.
La prova s'ha comentat en lloc d'eliminar-la perquè quede registrat el procés seguit.
publicclassFizzBuzzTest{@Test@DisplayName("FizzBuzz class should exist")voidclassFizzBuzzShouldExist(){FizzBuzzfizzBuzz=newFizzBuzz();}/* @Test @DisplayName("FizzBuzz::transform() method should exist") void fizzBuzzTransformMethodShouldExist() { FizzBuzz fizzBuzz = new FizzBuzz(); fizzBuzz.transform(); } */@Test@DisplayName("FizzBuzz::transform() method should have an int parameter")voidfizzBuzzTransformMethodShouldHaveIntParameter(){FizzBuzzfizzBuzz=newFizzBuzz();fizzBuzz.transform(1);}}
Es crea la quarta prova, que comprova el primer cas: transform(1) retorna 1.
@Test@DisplayName("FizzBuzz::transform(1) should return 1")voidfizzBuzzTransform_givenValue1_shouldReturn1(){FizzBuzzfizzBuzz=newFizzBuzz();intresult=fizzBuzz.transform(1);assertEquals(1,result);}
Es crea la cinquena prova, que comprova el segon cas: transform(2) retorna 2.
@Test@DisplayName("FizzBuzz::transform(2) should return 2")voidfizzBuzzTransform_givenValue2_shouldReturn2(){FizzBuzzfizzBuzz=newFizzBuzz();intresult=fizzBuzz.transform(2);assertEquals(2,result);}
Aquesta solució passa la prova nova, però no l'anterior: si es passa el valor 1 al mètode transform(),
retorna 2 en lloc de 1. Per tant, la solució no és vàlida. Gràcies a la prova anterior, s'ha detectat
que la solució no és vàlida, i així s'ha tret rendiment a la prova escrita.
Una solució que sí que funciona per a les dues proves és la següent:
Totes les proves passen, però cal plantejar-se si la solució és òptima.
En aquest cas, es pot refactoritzar el codi perquè siga més llegible i funcione en el cas general
(en qualsevol altre cas, retorna el nombre original):
A continuació, es prova el primer cas especial: si el nombre és divisible per 3, retorna Fizz.
Per tant, es crea la prova següent:
@Test@DisplayName("FizzBuzz::transform(3) should return Fizz")voidfizzBuzzTransform_givenValue3_shouldReturnFizz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(3);assertEquals("Fizz",result);}
Aquest canvi fa que les proves anteriors fallen, ja que esperen un int i ara reben un String.
Per tant, cal modificar les proves anteriors perquè esperen un String:
@Test@DisplayName("FizzBuzz::transform(1) should return 1")voidfizzBuzzTransform_givenValue1_shouldReturn1(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(1);assertEquals("1",result);}
En aquesta iteració es podria comprovar el cas 4, que hauria de retornar 4.
@Test@DisplayName("FizzBuzz::transform(4) should return 4")voidfizzBuzzTransform_givenValue4_shouldReturn4(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(4);assertEquals("4",result);}
No obstant això, si s'executa aquesta prova, s'observa que passa directament. Per tant, la funcionalitat
que comprova ja s'ha provat anteriorment i no cal afegir-la.
@Test@DisplayName("FizzBuzz::transform(5) should return Buzz")voidfizzBuzzTransform_givenValue5_shouldReturnBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(5);assertEquals("Buzz",result);}
@Test@DisplayName("FizzBuzz::transform(6) should return Fizz")voidfizzBuzzTransform_givenValue6_shouldReturnFizz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(6);assertEquals("Fizz",result);}
@Test@DisplayName("FizzBuzz::transform(10) should return Buzz")voidfizzBuzzTransform_givenValue10_shouldReturnBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(10);assertEquals("Buzz",result);}
Per aconseguir que la prova passe, cal modificar el mètode transform() perquè retorne Buzz quan
el paràmetre és 10. Seguint el raonament de la iteració anterior, s'utilitza directament el mòdul (%)
per comprovar si el nombre és divisible per 5.
Es prova el cas 15, que hauria de retornar FizzBuzz.
@Test@DisplayName("FizzBuzz::transform(15) should return FizzBuzz")voidfizzBuzzTransform_givenValue15_shouldReturnFizzBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(15);assertEquals("FizzBuzz",result);}
Si s'executa aquesta prova, s'observa que falla, ja que el mètode transform() retorna Fizz quan el paràmetre
és 15, en lloc de FizzBuzz. Això es deu al fet que la condició n % 3 == 0 és certa i, per tant, retorna Fizz
sense avaluar la resta de condicions. Cal canviar l'ordre de les condicions perquè la condició n == 15
es comprove abans que n % 3 == 0:
L'últim cas que queda per comprovar és el 30, que hauria de retornar FizzBuzz.
@Test@DisplayName("FizzBuzz::transform(30) should return FizzBuzz")voidfizzBuzzTransform_givenValue30_shouldReturnFizzBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(30);assertEquals("FizzBuzz",result);}
packageud4.tdd;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;import staticorg.junit.jupiter.api.Assertions.assertEquals;publicclassFizzBuzzTest{@Test@DisplayName("FizzBuzz class should exist")voidfizzBuzzClassShouldExist(){FizzBuzzfizzBuzz=newFizzBuzz();}/* @Test @DisplayName("FizzBuzz::transform() method should exist") void fizzBuzzTransformMethodShouldExist(){ FizzBuzz fizzBuzz = new FizzBuzz(); fizzBuzz.transform(); } */@Test@DisplayName("FizzBuzz::transfrom() should have an int parameter")voidfizzBuzzTransformShouldHaveIntParameter(){FizzBuzzfizzBuzz=newFizzBuzz();fizzBuzz.transform(1);}@Test@DisplayName("FizzBuzz::transform(1) should return 1")voidfizzBuzzTransform_given1_shouldReturn1(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(1);assertEquals("1",result);}@Test@DisplayName("FizzBuzz::transform(2) should return 2")voidfizzBuzzTransform_given2_shouldReturn2(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(2);assertEquals("2",result);}@Test@DisplayName("FizzBuzz::transform(3) should return Fizz")voidfizzBuzzTransform_given3_shouldReturnFizz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(3);assertEquals("Fizz",result);}@Test@DisplayName("FizzBuzz::transform(5) should return Buzz")voidfizzBuzzTransform_given5_shouldReturnBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(5);assertEquals("Buzz",result);}@Test@DisplayName("FizzBuzz::transform(6) should return Fizz")voidfizzBuzzTransform_given6_shouldReturnFizz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(6);assertEquals("Fizz",result);}@Test@DisplayName("FizzBuzz::transform(10) should return Buzz")voidfizzBuzzTransform_given10_shouldReturnBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(10);assertEquals("Buzz",result);}@Test@DisplayName("FizzBuzz::transform(15) should return FizzBuzz")voidfizzBuzzTransform_given15_shouldReturnFizzBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(15);assertEquals("FizzBuzz",result);}@Test@DisplayName("FizzBuzz::transform(30) should return FizzBuzz")voidfizzBuzzTransform_given30_shouldReturnFizzBuzz(){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(30);assertEquals("FizzBuzz",result);}}
FizzBuzzParametizedTest.java
packageud4.tdd;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;importorg.junit.jupiter.params.ParameterizedTest;importorg.junit.jupiter.params.provider.CsvSource;import staticorg.junit.jupiter.api.Assertions.assertEquals;publicclassFizzBuzzParametizedTest{@Test@DisplayName("FizzBuzz class should exist")voidfizzBuzzClassShouldExist(){FizzBuzzfizzBuzz=newFizzBuzz();}/* @Test @DisplayName("FizzBuzz::transform() method should exist") void fizzBuzzTransformMethodShouldExist(){ FizzBuzz fizzBuzz = new FizzBuzz(); fizzBuzz.transform(); } */@Test@DisplayName("FizzBuzz::transfrom() should have an int parameter")voidfizzBuzzTransformShouldHaveIntParameter(){FizzBuzzfizzBuzz=newFizzBuzz();fizzBuzz.transform(1);}@ParameterizedTest(name="FizzBuzz::transform({0}) should return {1}")@DisplayName("FizzBuzz::transform()")@CsvSource({"1, 1","2, 2","3, Fizz","5, Buzz","6, Fizz","10, Buzz","15, FizzBuzz","30, FizzBuzz"})voidfizzBuzzTransform(intinput,Stringexpected){FizzBuzzfizzBuzz=newFizzBuzz();Stringresult=fizzBuzz.transform(input);assertEquals(expected,result);}}
Aquest exemple ha mostrat el procés de desenvolupament d'un problema amb TDD. D'aquest procés
es poden extraure les conclusions següents:
Utilitat de les proves: les proves permeten comprovar el funcionament del codi i detectar errors
que potser no s'haurien detectat sense elles.
Desenvolupament guiat: les proves han guiat el desenvolupament, han aportat una utilitat immediata
i han permés refactoritzar el codi amb seguretat.
Evolució segura: si s'afig una regla nova, es poden afegir proves noves i modificar el codi amb la seguretat
que la funcionalitat anterior continuarà sent correcta.
Documentació viva: la classe FizzBuzzTest documenta el comportament de la classe FizzBuzz.