Une gestion d’erreur de son API .Net automatique via ses exceptions
...
Sommaire
Lorsqu’on écrit nos tests unitaires, on peut avoir l’impression que certains cas de tests sont quasiment identiques dans leur structure. Ils sont rébarbatifs à lire/écrire. C’est là que la notion de dataProvider est utile: on va réutiliser le même test mais avec des paramètres d’entrée différents 😉
Si on prend le cas du Kata Tennis (https://codingdojo.org/kata/Tennis/) , on a la situation suivante:
On a le premier joueur qui marque x fois, le second joueur qui marque x fois et enfin on compare avec le résultat attendu.
Les tests peuvent vite se multiplier et se ressembler:
@Test void shouldBe15LoveWhenPlayerOneScore() { Game game = new Game(); game.playerOneScoresAPoint(); String score = game.printScore(); assertThat(score).isEqualTo("15 - LOVE"); } @Test void shouldBe15-15WhenBothPlayersScore() { Game game = new Game(); game.playerOneScoresAPoint(); game.playerTwoScoresAPoint(); String score = game.printScore(); assertThat(score).isEqualTo("15 - 15"); } @Test void shouldBe30LoveWhenPlayerOneScore() { Game game = new Game(); game.playerOneScoresAPoint(); game.playerOneScoresAPoint(); String score = game.printScore(); assertThat(score).isEqualTo("30 - LOVE"); }
C’est assez rébarbatif et on se retrouve rapidement avec une classe de test qui fait plusieurs dizaines / centaines de lignes. On va voir dans cet article comment rendre nos tests plus lisibles et maintenables grâce aux tests paramétrés 😀
Junit Jupiter Params est trouvable ici: https://search.maven.org/search?q=a:junit-jupiter-params
Maven:
La dépendance est embarquée par
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>RELEASE</version> <scope>test</scope> </dependency>
Ou vous pouvez l’inclure spécifiquement:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>RELEASE</version> </dependency>
Cette bibliothèque va nous permettre de passer des paramètres d’entrées à nos tests. Ces paramètres proviendront d’une source de données (une méthode, un CSV, un tableau etc..).
Pour marquer un test comme un test paramétré, on va utiliser l’annotation @ParametrizedTest.
Exemple:
@ParameterizedTest void shouldGetGameScore(int timesPlayerOneScores, int timesPlayerTwoScores, String expectedGameScore) {
Ensuite, on doit décrire d’où proviennent les données d’entrées (notre data-provider) avec une autre annotation.
Avec @MethodSource j’indique que mes données viennent de la méthode nommée “gameProvider”.
Exemple:
@ParameterizedTest @MethodSource("gameProvider") void shouldGetGameScore(int timesPlayerOneScores, int timesPlayerTwoScores, String expectedGameScore) {
Avec ma méthode:
public static Stream<Arguments> gameProvider() { return Stream.of( Arguments.of(0, 0, "LOVE - LOVE"), Arguments.of(1, 0, "15 - LOVE"), Arguments.of(2, 0, "30 - LOVE"), Arguments.of(3, 0, "40 - LOVE"), Arguments.of(0, 1, "LOVE - 15"), Arguments.of(2, 3, "30 - 40"), Arguments.of(3, 3, "DEUCE") ); }
Un test unitaire sera exécuté par chaque argument. Par exemple, pour le premier argument Arguments.of(0, 0, « LOVE – LOVE »), mon test unitaire sera lancé avec:
– Le premier paramètre (timesPlayerOneScores) qui vaut 0
– Le second paramètre (timesPlayerTwoScores) qui vaut 0 également
– Le dernier paramètre (expectedGameScore) qui a pour valeur “LOVE – LOVE”
Note: D’autres façons de transmettre de la donnée existent. On peut lire un CSV, une énum ou même donner un tableau d’entrée.
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; class TennisTest { private Game game; @ParameterizedTest @MethodSource("gameProvider") void shouldGetGameScore(int timesPlayerOneScores, int timesPlayerTwoScores, String expectedGameScore) { game = new Game(); playerOneScores(timesPlayerOneScores); playerTwoScores(timesPlayerTwoScores); String score = game.printScore(); assertThat(score).isEqualTo(expectedGameScore); } private void playerOneScores(int timesPlayerOneScores) { for (int i = 0; i < timesPlayerOneScores; i++) { game.playerOneScoresAPoint(); } } private void playerTwoScores(int timesPlayerTwoScores) { for (int i = 0; i < timesPlayerTwoScores; i++) { game.playerTwoScoresAPoint(); } } public static Stream<Arguments> gameProvider() { return Stream.of( Arguments.of(0, 0, "LOVE - LOVE"), Arguments.of(1, 0, "15 - LOVE"), Arguments.of(2, 0, "30 - LOVE"), Arguments.of(3, 0, "40 - LOVE"), Arguments.of(0, 1, "LOVE - 15"), Arguments.of(2, 3, "30 - 40"), Arguments.of(3, 3, "DEUCE") ); } }
En lançant mes tests, on a bien le détail de l’exécution:
Si vous avez la sensation d’écrire plusieurs fois des tests similaires, un test paramétrés pourra peut-être vous permettre de gagner en temps d’écriture et en lisibilité. C’est un outil que j’utilise au quotidien et j’espère que je vous aurais convaincu de son intérêt 😀
On va voir comment avoir en quelques minutes des assertions qui vont vérifier les endpoints de notre API avec des scénarios de ce genre: Feature: Create a new account As a visitor, I can create an account to access the game Scenario: A visitor c...
Depuis .NET 9, le le support d’OpenAPI est directement inclus dans .NET et ne passe plus par les librairies Swagger par défaut (plus d’info sur ce choix ici si jamais ça vous intéresse). De façons simplifiée, la librairie Swashbuckle.AspNetCore.Sw...
Description du problème Par défaut, il n’est pas autorisé de faire des requêtes entre une application qui est dans un domaine A vers une autre qui serait dans un domaine B (pour des raisons de sécurité, il y a plus de détails dans les sources). S...