Rendre le test lisible et maintenable est un défi pour tout ingénieur logiciel. Parfois, une portée de test devient encore plus importante lorsque nous devons créer un objet complexe ou recevoir des informations d’autres points, tels qu’une base de données, un service Web ou un fichier de propriétés. Vous pouvez utiliser la simplicité en séparant la création d’objet de la portée du test à l’aide de la JUnitParams
. Dans ce didacticiel vidéo, nous allons apprendre à utiliser JUnitParams
les types et comment simplifier vos tests avec ces techniques.
La première question qui pourrait vous venir à l’esprit est : « Pourquoi devrais-je séparer l’objet de ma portée de test ? » Nous allons commencer à énumérer certains points et quand cela a du sens.
- Evitez la complexité : Finalement, vous devez créer des instances qui peuvent varier selon le contexte, alors prenez ces informations à partir d’une base de données, de microservices, etc. Pour vous faciliter la tâche, vous pouvez diviser pour mieux régner, vous éloignant ainsi de ce test.
- Définir la portée: Pour se concentrer sur le test ou augmenter la cohésion du test, vous pouvez scinder et recevoir les paramètres de l’injection.
Le but ici n’est pas d’inciter à utiliser cette injection de paramètres sur tous les tests, mais une fois que les paramètres sont complexes et que vous devez tester le même scénario avec différents tests, ce sont de bons candidats pour l’explorer.
JUnitParams
est une extension qui peut vous aider dans ces cas. Vous devez ajouter cette dépendance à votre projet. Le code ci-dessous montre la dépendance à un projet Maven.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
Avec cette dépendance, nous devrons remplacer le traditionnel Test
annotation avec ParameterizedTest
et indiquez à la source où et comment ces paramètres seront injectés. Dans cet article, nous énumérerons trois manières, toutes accompagnées des annotations suivantes :
ValueSource
: Cette source fonctionne avec des valeurs littérales directement sur l’annotation.MethodSource
: vous pouvez utiliser des méthodes sur la portée de la classe pour alimenter ce paramètre.ArgumentSource
: Si vous le souhaitez, vous pouvez appliquer la responsabilité unique ici, où vous pouvez avoir une classe pour fournir les arguments pour le test.
Pour le mettre en pratique, explorons le scénario de l’équipe de football. Nous voulons tester la règle métier de l’équipe, où nous devons nous assurer de la quantité de l’équipe, du nom de l’équipe, etc.
Commençons par la source la plus accessible, la ValueSource
. Nous pouvons exécuter le même test avec des valeurs différentes. Un autre point est qu’avec le ParameterizedTest
, vous pouvez définir le nom du test à l’aide du paramètre. Utilisons-le pour tester le nom de l’équipe.
Le code ci-dessous montre le name
test, ce qui devrait créer un team
avec le name
et faire correspondre la valeur du paramètre. Le test s’exécutera deux fois : vous verrez deux tests avec des noms différents.
@ParameterizedTest(name = "Should create a team with the name {0}")
@ValueSource(strings = {"Bahia", "Santos"})
public void shouldCreateTeam(String name) {
Team team = Team.of(name);
org.assertj.core.api.Assertions.assertThat(team)
.isNotNull().extracting(Team::name)
.isEqualTo(name);
}
La deuxième source est la MethodSource
, où nous pouvons mettre des objets plus complexes et les créer par programmation. Le retour de cette méthode utilise les arguments qui sont une interface de JUnit.
Le test ci-dessous testera un Team
avec un player
où nous vérifierons que étant donné un player
il entrera dans le team
.
@ParameterizedTest
@MethodSource("players")
public void shouldCreateTeamWithAPlayer(Player player) {
Assertions.assertNotNull(player);
Team bahia = Team.of("Bahia");
bahia.add(player);
org.assertj.core.api.Assertions.assertThat(bahia.players())
.hasSize(1)
.map(Player::name)
.contains(player.name());
}
static Collection<Arguments> players() {
return List.of(Arguments.of(Player.of("Neymar", "Santos", 11)));
}
Le dernier que nous allons explorer aujourd’hui est ArgumentSource
, où vous pouvez avoir une classe pour se concentrer sur la fourniture de ces arguments. Le troisième et dernier test le créera. Nous allons tester la somme des scores dans une équipe de football.
La première étape consiste à créer une classe qui implémente ArgumentsProvider
interface. Cette interface reçoit un context-param
où vous pouvez révéler des informations utiles telles que des balises et des noms d’affichage. Ainsi, utilisez-le comme si la balise était « base de données » et prenez la source de la base de données. Dans notre premier test, nous ne l’utiliserons pas.
public class PlayerProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
List<Player> players = new ArrayList<>();
players.add(Player.of("Poliana", "Salvador", 20));
players.add(Player.of("Otavio", "Salvador", 0));
return Stream.of(Arguments.of(players));
}
}
La première étape consiste à l’utiliser sur la source, ce qui est assez similaire aux autres sources utilisant l’annotation :
@ParameterizedTest
@ArgumentsSource(PlayerProvider.class)
public void shouldCreateTotalScore(List<Player> players) {
Team team = Team.of("Leiria");
players.forEach(team::add);
int score = team.score();
int playerScore = players.stream().mapToInt(Player::score)
.sum();
Assertions.assertEquals(playerScore, score);
}
C’est ça!
La vidéo ci-dessous vise à explorer davantage la capacité d’injecter des paramètres dans le test en utilisant JUnitParams
. Les trois types de sources ne sont qu’un début. J’espère que cela vous inspirera pour rendre votre code lisible avec le param
aptitude.