Pour mon application Spring-Boot, je fournis un RestTemplate via un fichier @Configuration afin de pouvoir ajouter des valeurs par défaut sensibles (ex Timeouts). Pour mes tests d’intégration, je voudrais me moquer de RestTemplate car je ne souhaite pas me connecter à des services externes - je sais quelles réponses attendre. J’ai essayé de fournir une implémentation différente dans le package d’intégration-test dans l’espoir que cette dernière remplace la réelle implémentation, mais en vérifiant les journaux, c’est l’inverse: la vraie implémentation remplace celle de test.
Comment puis-je m'assurer que celui de TestConfig est celui utilisé?
Ceci est mon fichier de configuration:
@Configuration
public class RestTemplateProvider {
private static final int DEFAULT_SERVICE_TIMEOUT = 5_000;
@Bean
public RestTemplate restTemplate(){
return new RestTemplate(buildClientConfigurationFactory());
}
private ClientHttpRequestFactory buildClientConfigurationFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT);
factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT);
return factory;
}
}
Test d'intégration:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@ActiveProfiles("it")
public abstract class IntegrationTest {}
Classe TestConfiguration:
@Configuration
@Import({Application.class, MockRestTemplateConfiguration.class})
public class TestConfiguration {}
Et enfin MockRestTemplateConfiguration
@Configuration
public class MockRestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
Depuis Spring Boot 1.4.x, il existe une option permettant d'utiliser l'annotation @MockBean
pour simuler des beans Spring.
Réaction au commentaire:
Pour conserver le contexte dans le cache, n'utilisez pas @DirtiesContext
, mais utilisez @ContextConfiguration(name = "contextWithFakeBean")
. Il créera un contexte séparé, tout en conservant le contexte par défaut dans le cache. Spring gardera les deux (ou le nombre de contextes que vous avez) en cache.
Notre construction est la suivante: la plupart des tests utilisent la configuration par défaut non polie, mais nous avons 4-5 tests qui simulent des haricots. Le contexte par défaut est bien réutilisé
1 . Vous pouvez utiliser l'annotation @Primary
:
@Configuration
public class MockRestTemplateConfiguration {
@Bean
@Primary
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
BTW, j'ai écrit blog post à propos de fake Spring bean
2 . Mais je suggérerais de jeter un œil au support de test de Spring RestTemplate . Ce serait un exemple simple: private MockRestServiceServer mockServer;
@Autowired
private RestTemplate restTemplate;
@Autowired
private UsersClient usersClient;
@BeforeMethod
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
@Test
public void testSingleGet() throws Exception {
// GIVEN
int testingIdentifier = 0;
mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON));
// WHEN
User user = usersClient.getUser(testingIdentifier);
// THEN
mockServer.verify();
assertEquals(user.getName(), USER0_NAME);
assertEquals(user.getEmail(), USER0_EMAIL);
}
Plus d'exemples peuvent être trouvés dans mon rapport Github ici
Approfondissant un peu, voir ma deuxième réponse .
J'ai résolu le problème en utilisant
@SpringBootTest(classes = {AppConfiguration.class, AppTestConfiguration.class})
au lieu de
@Import({ AppConfiguration.class, AppTestConfiguration.class });
Dans mon cas, le test n'est pas dans le même package que l'application. Je dois donc spécifier explicitement AppConfiguration.class (ou App.class). Si vous utilisez le même package dans le test, alors vous pouvez simplement écrire
@SpringBootTest(classes = AppTestConfiguration.class)
est pas de (ne fonctionne pas)
@Import(AppTestConfiguration.class );
C’est joli de voir que c’est tellement différent. Peut-être que quelqu'un peut expliquer cela. Je ne pouvais pas trouver de bonnes réponses jusqu'à maintenant. Vous pensez peut-être que @Import(...)
n'est pas détecté si @SpringBootTests
est présent, mais que le bean de remplacement apparaît dans le journal. Mais juste dans le mauvais sens.
En passant, utiliser @TestConfiguration
à la place de @Configuration
ne fait également aucune différence.
Le problème dans votre configuration est que vous utilisez @Configuration
pour votre configuration de test. Cela remplacera votre configuration principale. Utilisez plutôt @TestConfiguration
qui ajoutera (remplacera) votre configuration principale.
46.3.2 Détection de la configuration de test
Si vous souhaitez personnaliser la configuration principale, vous pouvez utiliser un fichier classe @TestConfiguration imbriquée. Contrairement à une classe @Configuration imbriquée, qui serait utilisé à la place du fichier primaire de votre application. configuration, une classe imbriquée @TestConfiguration est utilisée en plus à la configuration principale de votre application.
Exemple utilisant SpringBoot:
Classe principale
@SpringBootApplication() // Will scan for @Components and @Configs in package tree
public class Main{
}
Config principale
@Configuration
public void AppConfig() {
// Define any beans
}
Test config
@TestConfiguration
public void AppTestConfig(){
// override beans for testing
}
Classe de test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Main.class, AppTestConfig.class })
public void AppTest() {
// use @MockBean if you like
}
Remarque: Utiliser @Import(AppTestConfig.class)
à la place ne remplacera pas un bean de AppConfig
. D'une manière ou d'une autre, il sera simplement ajouté si le haricot est manquant. Appréciez tout lien vers une documentation officielle, cela le rend clair.