2017.07.02 Modifié.
Dans la continuité de la Préparation postée l'autre jour, nous allons cette fois procéder avec le TDD du problème FizzBuzz en version pratique.
Exigences | Contenu |
---|---|
Exigence 1 | Renvoie la valeur prise comme argument sous forme de chaîne de caractères |
Exigence 2 | Cependant, s'il s'agit d'un multiple de 3, "Fizz" est renvoyé. |
Exigence 3 | Cependant, s'il s'agit d'un multiple de 5, "Buzz" est renvoyé. |
Exigence 4 | Cependant, s'il est un multiple de 3 et 5, il renvoie "FizzBuzz". |
Exigence 5 | Erreur si l'argument n'est pas un nombre compris entre 1 et 100 |
Puisqu'il serait redondant d'introduire toutes les exigences 1 à 5, seule l'exigence 1 sera introduite dans cet article. Le programme complet est publié à la fin de l'article.
Dans l'implémentation de l'exigence 1, la classe de test (FizzBuzzTest.java) et la classe cible de test (FizzBuzz.java) ont d'abord été implémentées comme suit.
FizzBuzzTest.java
public class FizzBuzzTest {
@Test
public void Renvoie 1 si 1 est donné comme argument() {
FizzBuzz fizzbuzz = new FizzBuzz();
assertEquals("1", fizzbuzz.response(1));
}
}
FizzBuzz.java
public class FizzBuzz {
//Renvoie null(Ne renvoie aucune valeur)
public String response(int num) {
return null;
}
}
Déplacez le curseur sur FizzBuzzTest, faites un clic droit-> Exécuter en tant que… -> appuyez sur JUnit Test, et lorsque vous exécutez le test, le résultat sera naturellement une barre rouge indiquant l'échec du test.
Cet ** échec de test ** est en fait le point. Lorsque vous développez une nouvelle classe à tester comme cette fois, vous pouvez vérifier si ** JUnit juge correctement le succès ou l'échec du test **.
De plus, si vous ajoutez un nouveau test et que le test réussit lorsque vous ajoutez une fonction à la ** méthode existante de la classe testée, il est possible que certaines des fonctions supplémentaires aient déjà été implémentées. Voir à travers le sexe.
Ensuite, implémentez la méthode testée qui passera le test.
FizzBuzz.java
public String response(int num) {
return "1";
}
Si vous exécutez le test comme précédemment, le test réussira bien sûr.
Dans le développement de TDD, nous commençons par ** écrire un programme qui passe le test **, donc jusqu'à ce que nous nous y habituions, nous ne réduirons pas les cas de test depuis le début, et même si nous sommes un peu stupides, nous allons construire pour atteindre chaque objectif un par un.
Essayons maintenant même si l'argument est 2.
FizzBuzzTest.java
@Test
public void Renvoie 1 si 1 est donné comme argument() {
FizzBuzz fizzbuzz = new FizzBuzz();
assertEquals("1", fizzbuzz.response(1));
}
@Test
public void Renvoie 2 si 2 est donné comme argument() {
FizzBuzz fizzbuzz = new FizzBuzz();
assertEquals("2", fizzbuzz.response(2));
}
FizzBuzz.java
public String response(int num) {
return "1";
}
Le résultat de l'exécution du test est le suivant et une barre rouge indiquant l'échec du test s'affiche.
Il y a généralement deux possibilités de l'échec du test ajouté:
En regardant le résultat de l'exécution de JUnit, nous pouvons voir dans la description suivante que le résultat de l'exécution était 1 même si nous nous attendions à ce que le résultat de l'exécution soit 2, ce qui ne correspond pas à la valeur attendue.
org.junit.ComparisonFailure: expected:<[2]> but was:<[1]>
Par conséquent, modifiez le programme cible de test FizzBuzz.java comme suit en fonction de l'exigence: la valeur prise comme argument est renvoyée sous forme de chaîne de caractères.
FizzBuzz.java
public String response(int num) {
return String.valueOf(num);
}
Après avoir confirmé que le test est réussi, la mise en œuvre de l'exigence 1 est terminée.
De même, FizzBuzzTest.java et FizzBuzz.java, qui implémentent les exigences 2 à 5 et réduisent les cas de test de la classe équivalente à un, sont les suivants.
FizzBuzzTest.java
public class FizzBuzzTest {
private FizzBuzz fizzbuzz;
@Before
instanciation de public void() {
fizzbuzz = new FizzBuzz();
}
@Test
public void Renvoie 1 si 1 est donné comme argument() {
assertEquals("1", fizzbuzz.response(1));
}
@Test
public void Renvoie Fizz avec 3 arguments() {
assertEquals("Fizz", fizzbuzz.response(3));
}
@Test
public void Renvoie Buzz avec 5 arguments() {
assertEquals("Buzz", fizzbuzz.response(5));
}
@Test
public void Renvoie FizzBuzz avec 15 arguments() {
assertEquals("FizzBuzz", fizzbuzz.response(15));
}
@Test(expected = IndexOutOfBoundsException.class)
public void Si 0 est donné à l'argument, une erreur se produira.() {
fizzbuzz.response(0);
}
@Test(expected = IndexOutOfBoundsException.class)
Si 101 est donné à l'argument public void, une erreur se produira.() {
fizzbuzz.response(101);
}
@Test
public void Renvoie Buzz si 100 est donné comme argument() {
assertEquals("Buzz", fizzbuzz.response(100));
}
}
FizzBuzz.java
public class FizzBuzz {
public String response(int num) {
if(num < 1 || num > 100) {
throw new IndexOutOfBoundsException();
}
StringBuilder result = new StringBuilder();
if(num % 3 == 0) {
result.append("Fizz");
}
if(num % 5 == 0) {
result.append("Buzz");
}
if(result.length() == 0) {
result.append(String.valueOf(num));
}
return result.toString();
}
}
À partir du nom de la méthode de test FizzBuzzTest.java ci-dessus, vous pouvez savoir ce que le test doit vérifier, vous pouvez donc le terminer tel quel, mais il est plus facile de comprendre ** à quelle exigence correspond chaque méthode de test ** Enfin, ** structurez la classe de test **.
La structuration des classes de test consiste à utiliser l'annotation @RunWith (Enclosed.class) de JUnit pour définir une classe interne pour chaque catégorie de test afin de classer les cas de test. [^ 1] [^ 2]
Ce test peut être divisé dans les catégories suivantes en fonction des exigences.
Numéro d'article | Catégorie |
---|---|
1 | Les arguments ne sont pas des multiples de 3 et 5 |
2 | Un multiple avec seulement 3 arguments |
3 | Un multiple avec seulement 5 arguments |
4 | Les arguments sont des multiples de 3 et 5 |
5 | L'argument est une valeur limite non valide(Pas un nombre de 1 à 100) |
6 | L'argument est une valeur limite valide(Nombres de 1 à 100) |
Définissez la classe interne pour chacune des catégories ci-dessus, puis réécrivez FizzBuzzTest.java comme suit.
FizzBuzzTest.java
@RunWith(Enclosed.class)
public class FizzBuzzTest {
L'argument de classe statique publique n'est pas un multiple de 3 et 5{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test
public void Renvoie 1 si 1 est donné comme argument() {
assertEquals("1", fizzbuzz.response(1));
}
}
public static class Un multiple de 3 avec seulement 3 arguments{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test
public void Renvoie Fizz avec 3 arguments() {
assertEquals("Fizz", fizzbuzz.response(3));
}
}
public static class Un multiple de 5 avec seulement 5 arguments{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test
public void Renvoie Buzz avec 5 arguments() {
assertEquals("Buzz", fizzbuzz.response(5));
}
}
public static class Les arguments sont des multiples de 3 et 5{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test
public void Renvoie FizzBuzz avec 15 arguments() {
assertEquals("FizzBuzz", fizzbuzz.response(15));
}
}
L'argument de classe statique public n'est pas valide. Valeur limite{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test(expected = IndexOutOfBoundsException.class)
public void Si 0 est donné à l'argument, une erreur se produira.() {
fizzbuzz.response(0);
}
@Test(expected = IndexOutOfBoundsException.class)
Si 101 est donné à l'argument public void, une erreur se produira.() {
fizzbuzz.response(101);
}
}
l'argument de classe statique public est une valeur limite valide{
FizzBuzz fizzbuzz = new FizzBuzz();
@Test
public void Renvoie 1 si 1 est donné comme argument() {
assertEquals("1", fizzbuzz.response(1));
}
@Test
public void Renvoie Buzz si 100 est donné comme argument() {
assertEquals("Buzz", fizzbuzz.response(100));
}
}
}
Bien sûr, après la réécriture, exécutez le test pour vous assurer que tout est réussi.
Je pense que les résultats des tests permettent également de comprendre plus facilement à quelles exigences les cas de test sont destinés, plutôt que les cas de test étant alignés dans une rangée.
C'est la fin de l'édition pratique. Cette fois, c'était TDD pour des exigences relativement simples, mais à l'avenir, je vais certainement essayer TDD lors de la mise en œuvre d'exigences plus complexes et voir ses effets et ses limites.
[^ 1]: L'annotation @RunWith (Enclosed.class) reconnaît toutes les classes internes comme étant testées et leur permet d'exécuter les méthodes avec l'annotation @Test dans chaque classe interne.
[^ 2]: Si vous n'aimez pas la classe interne, vous pouvez créer une méthode de test pour chaque catégorie de test et exprimer le cas de test en définissant plusieurs assertions.
Recommended Posts