1. Introduction
Il y a une utilisation intensive des annotations dans Spring Framework. Cela crée une confusion sur les annotations à utiliser, en particulier dans les tests. Cela finit parfois par ajouter des annotations redondantes ou que les tests ne fonctionnent pas comme prévu. Sur StackOverflow, vous pouvez facilement trouver les développeurs qui font des erreurs avec les annotations Spring ou quelqu’un qui fait fonctionner les choses mais ne comprend pas complètement pourquoi.
Pour clarifier les choses, dans cet article, je résumerais les annotations que nous devrions utiliser dans chaque scénario de test à l’aide de JUnit 5 et Spring Test Framework.
2. Spring Framework et Spring Boot
Avant d’examiner le test Spring, nous devons savoir comment fonctionne Spring et la relation Spring Framework et Spring Boot.
Spring est le framework d’application le plus populaire de Java. Il simplifie le développement Java EE en fournissant une fonction d’injection de dépendance et prend en charge de nombreuses technologies populaires telles que Spring JDBC, Spring MVC, Spring Test.
Pour démarrer une application Spring, vous devez créer un ApplicationContext qui est un conteneur IoC de tous les objets bean utilisés dans l’application. Voici un exemple:
@Configuration
// This is the primary configuration class of the application. From here, Spring
// will scan all declared components and make them available in ApplicationContext.
// We can use @Import(OtherConfig.class) to import other configurations
// into the primary one.
// We can also use @ComponentScans (e.g. @ComponentScans("services"))
// to ask Spring to scan for all components (e.g. classes having @Component, @Service)
// in a package and add them into the service container (i.e. ApplicationContext)
public class Config {
@Bean
public HelloService helloService() {
// assume we have HelloService interface and HelloServiceImpl class
return new HelloServiceImp();
}
}
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Config.class);
// Use container API to get back the service we need
var helloService = context.getBean(HelloService.class);
System.out.print(helloService.hi());
}
}
À partir de l’exemple ci-dessus, vous pouvez prévoir que nous devons effectuer de nombreuses étapes manuelles pour mettre en place une application Spring, en particulier l’application d’entreprise avec de nombreuses dépendances externes telles que la base de données, la file d’attente de messages, les API tierces.
Spring Boot facilite les choses en effectuant toutes les configurations automatiques pour nous. Voici le code pour le même exemple dans Spring Boot :
@SpringBootApplication
public class MainApplication {
@Bean
public HelloService helloService() {
return new HelloServiceImp();
}
public static void main(String[] args) {
Application context = SpringApplication.run(MainApplication.class);
var helloService = context.getBean(HelloService.class);
System.out.println(helloService.hi());
}
}
En regardant dans l’annotation SpringBootApplication, vous pouvez voir qu’il existe une méta-annotation @SpringBootConfiguration qui inclut à nouveau @Configuration. Cela explique pourquoi Spring peut toujours trouver la classe de configuration principale et charger le bean HelloService avec `@SpringBootApplication`.
Spring a tendance à regrouper plusieurs annotations en une seule pour simplifier les choses. Ce regroupement génère de nombreuses nouvelles annotations qui rendent parfois les développeurs confus et ajoutent des annotations redondantes (par exemple, en ajoutant à la fois @SpringBootApplication et @Configuration à la même classe)
Visitez les liens suivants au cas où vous voudriez en savoir plus sur Spring Framework et Spring Boot :
Dans l’ensemble, nous devons avoir 1 configuration principale pour créer un ApplicationContext pour toute application Spring.
3. Écrire un test unitaire de base dans Spring Framework
Voici la configuration la plus basique pour écrire un test au printemps :
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes={TestConfig.class})
public class TodoServiceTest {
@Autowired
private TodoService todoService;
// ...
}
Avec ce code, un Spring ApplicationContext sera créé en utilisant TestConfig comme configuration principale. Il obtient ensuite l’instance TodoService du conteneur et l’injecte dans la classe de test. Cela correspond à ce que nous avons discuté jusqu’à présent : l’application Spring doit créer ApplicationContext à l’aide d’une configuration principale.
Il existe un court formulaire pour la configuration ci-dessus. Si vous regardez à l’intérieur de @SpringJUnitConfig, vous pouvez voir 2 méta-annotations : @ExtendWith et @ContextConfiguration
@SpringJUnitConfig(TestConfig.class)
public class TodoServiceTest {
// ...
}
Si la classe de configuration n’est pas spécifiée, Spring recherchera la configuration intégrée dans la classe de test
@SpringJUnitConfig
public class TodoServiceTest {
@Configuration
static class TestConfig {
@Bean
public TodoService todoService() {
// ...
}
}
// ...
}
Notez que, dans les deux cas, ApplicationContext n’est instancié qu’une seule fois et partagé entre toutes les méthodes de test de la classe.
Utilisation de @TestPropertySource pour transmettre des propriétés personnalisées à des fins de test. A une priorité plus élevée que les sources
@SpringJUnitConfig(TestConfig.class)
@TestPropertySource(properties={"username=foo", "password=bar"},
locations="classpath:todo-test.properties")
public class TodoServiceTests {
// ...
}
Veuillez cliquer sur ce lien pour voir le code complet.
4. Test avec Spring Boot
Spring Boot introduit de nouvelles annotations. Voici quelques-uns des plus populaires :
- @SpringBootTest
- @WebMvcTest
- @DataJpaTest, @DataJdbcTest, @JdbcTest
- @MockBean
4.1. Test d’intégration avec @SpringBootTest
Contrairement à @SpringJUnitConfig, @SpringBootTest, par défaut, démarre l’ensemble du contexte de l’application de la même manière que lorsque vous exécutez votre application Spring Boot. Avec cette annotation, Spring recherchera une classe avec @SpringBootConfiguration et l’utilisera comme configuration principale pour créer ApplicationContext. Il effectue également la configuration automatique de TestRestTemplate que nous pouvons connecter à la classe de test et utiliser pour appeler des API. Voici un exemple :
@SpringBootApplication // includes meta annotation @SpringBootConfiguration
public class SpringtestingApplication {
// ...
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TodoControllerTest {
@Autowired
TestRestTemplate restTemplate;
// ...
}
Veuillez cliquer sur ce lien pour voir le code complet.
4.2. Test unitaire pour la couche contrôleur avec @WebMvcTest
Les tests avec @WebMvcTest n’appliqueront que la configuration pertinente aux tests MVC. La configuration complète sera désactivée. Spring Test Framework configure également automatiquement MockMvc que nous pouvons injecter dans la classe de test et utiliser pour appeler des API testées.
@WebMvcTest(TodoController.class)
public class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TodoService todoService;
// ...
}
Veuillez cliquer sur ce lien pour voir le code complet.
5. Conclusion
Résumer:
- Utilisez @SpringJUnitConfig pour écrire un test unitaire
- Utilisez @WebMvcTest avec MockMvc pour écrire un test unitaire pour la couche de contrôleur
- Utilisez @SpringBootTest avec TestRestTemplate pour écrire un test d’intégration pour l’application Spring Boot