Dans mon application, j'ai beaucoup de services REST. J'ai écrit des tests pour tous les services avec:
org.springframework.web.client.RestTemplate
Une invocation de service REST, par ex. ressemble à ça:
final String loginResponse = restTemplate.exchange("http://localhost:8080/api/v1/xy", HttpMethod.POST, httpEntity, String.class)
.getBody();
et je vérifie le corps de la réponse après - tout fonctionne bien. L'inconvénient est que l'application doit être lancée pour invoquer les services REST.
Ma question serait maintenant de savoir comment je peux le faire dans mes méthodes JUnit- @Test? Il s'agit d'une application Spring Boot (avec Tomcat intégré).
Merci pour l'aide!
Il y a un bon chapitre à ce sujet dans la documentation, je vous suggère de le lire pour bien comprendre ce que vous pouvez faire.
J'aime utiliser @IntegrationTest
avec une configuration personnalisée car cela démarre le serveur entier et vous permet de tester le système complet. Si vous souhaitez remplacer certaines parties du système par des simulateurs, vous pouvez le faire en excluant certaines configurations ou beans et en les remplaçant par les vôtres.
Voici un petit exemple. J'ai omis l'interface MessageService
car il est évident d'après IndexController
ce qu'elle fait, et son implémentation par défaut - DefaultMessageService
- parce qu'elle n'est pas pertinente.
Ce qu'il fait, c'est qu'il fait tourner l'ensemble de l'application moins le DefaultMessageService
mais avec son propre MessageService
à la place. Il utilise ensuite RestTemplate
pour émettre des requêtes HTTP réelles vers l'application en cours d'exécution dans le scénario de test.
Classes d'application:
IntegrationTestDemo.Java:
@SpringBootApplication
public class IntegrationTestDemo {
public static void main(String[] args) {
SpringApplication.run(IntegrationTestDemo.class, args);
}
}
IndexController.Java:
@RestController
public class IndexController {
@Autowired
MessageService messageService;
@RequestMapping("/")
String getMessage() {
return messageService.getMessage();
}
}
Classes de test:
IntegrationTestDemoTest.Java:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfig.class)
@WebIntegrationTest // This will start the server on a random port
public class IntegrationTestDemoTest {
// This will hold the port number the server was started on
@Value("${local.server.port}")
int port;
final RestTemplate template = new RestTemplate();
@Test
public void testGetMessage() {
String message = template.getForObject("http://localhost:" + port + "/", String.class);
Assert.assertEquals("This is a test message", message);
}
}
TestConfig.Java:
@SpringBootApplication
@ComponentScan(
excludeFilters = {
// Exclude the default message service
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = DefaultMessageService.class),
// Exclude the default boot application or it's
// @ComponentScan will pull in the default message service
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = IntegrationTestDemo.class)
}
)
public class TestConfig {
@Bean
// Define our own test message service
MessageService mockMessageService() {
return new MessageService() {
@Override
public String getMessage() {
return "This is a test message";
}
};
}
}
Si vous ne cherchiez pas un test de bout en bout (intégration), le MockRestServiceServer
pourrait vous aider. J'ai trouvé qu'il était très utile de dissocier mes cas de test d'un vrai service.
Spring doc a dit:
Utilisé pour les tests impliquant une utilisation directe ou indirecte du RestTemplate. Fournit un moyen de configurer les demandes attendues qui seront effectuées via le RestTemplate ainsi que des réponses factices à renvoyer ainsi supprimant le besoin d'un serveur réel.
Voici le doc officiel
Un autre conseil est que requestTo
ne peut pas être importé automatiquement
server.expect(manyTimes(), requestTo("/hotels/42")) ....
C'est une méthode statique de org.springframework.test.web.client.match.MockRestRequestMatchers
Puisque vous utilisez Spring MVC pour REST, je recommanderais d'utiliser les fonctionnalités de test fournies en instanciant MockMVC () - permettant des tests tels que:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
... // any required Spring config
)
@WebAppConfiguration
public class RestControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void getUserList() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(content().encoding("UTF-8"))
.andExpect(jsonPath("$", hasSize(8)))
.andExpect(jsonPath("$[0].id").exists())
.andExpect(jsonPath("$[0].alias").exists())
.andExpect(jsonPath("$[0].name").exists())
);
}
}
Ce test unitaire testera une interface REST sans déploiement. Spécifiquement, si exactement 8 utilisateurs sont retournés et le premier a les champs 'id', 'alias' et 'name'.
Les assertions jsonPath nécessitent deux dépendances:
'com.jayway.jsonpath:json-path:0.8.1'
'com.jayway.jsonpath:json-path-assert:0.8.1'
Et probablement aussi:
'org.springframework:spring-test:4.1.7.RELEASE'