'How to test restarting Spring Boot microservices in Cucumber?
I want to test multiple Java microservices collaborating in Cucumber. In particular I want to test restarting one service and checking the other services handle the interruption when it is down and restarted. The cleanest way to simulate stopping the service is to close the child Spring context of the service. Without this and with calling various close methods there is a chance that not everything will be torn down, and better to test in the same way it is stopped in production. My question is - how can I get Cucumber to rebind the glue to the new Spring context when I restart the service?
I have 2 services, Service A and Service B. My feature test of restarting A is this:
Scenario: Test ServiceA and ServiceB
Given Service A says hello
Given Service B says hello
When Service A is stopped
Then Service B says hello
When Service A is started
And Service A says hello
The Service A glue allows us to stop ServiceA's Spring context and create a new one:
@SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true", "spring.main.web-application-type=NONE"},
classes = {
ServiceAConfig.class, ServiceBConfig.class
})
@EnableConfigurationProperties
public class ServiceASteps {
public ServiceASteps(){
System.out.println("ServiceASteps init");
}
@Autowired
ServiceA serviceA;
@Autowired
ConfigurableApplicationContext serviceAContext;
@Given("Service A says hello")
public void serviceASaysHello() {
serviceA.greet();
}
@When("Service A is stopped")
public void serviceAIsStopped() {
System.out.println("Stopping service A context");
serviceAContext.close();
}
@When("Service A is started")
public void serviceAIsStarted() {
System.out.println("Starting service A context");
serviceAContext = SpringApplication.run(ServiceAApp.class);
}
}
But of course the glue is still bound to the old bean and context after recreating it. How can I get Cucumber to bind to the new one?
ServiceA is a standard bean, ServiceAApp is a wrapper app for running the Spring boot app:
@SpringBootApplication
public class ServiceAApp {
public static void main(String[] args) {
SpringApplication.run(ServiceAApp.class, args);
}
@Autowired
ServiceA serviceA;
@PostConstruct
public void onPostConstruct(){
System.out.println("Started ServiceAApp " + serviceA);
}
@PreDestroy
public void onPreDestroy() {
System.out.println("Stopping ServiceAApp " + serviceA);
}
}
ServiceAConfig simply instantiates the bean:
@Configuration
@EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
ReactiveSecurityAutoConfiguration.class
})
@Import(SharedConfig.class)
public class ServiceAConfig {
@Bean
ServiceA serviceA(SharedService sharedService){
return new ServiceA();
}
@PostConstruct
public void start() {
}
@PreDestroy
public void close() {
}
}
(Ditto for B.)
Solution 1:[1]
The short answer is that you can't with cucumber-spring. Cucumber uses Springs TestContextManager which is not designed to deal with the restarting application context.
You could do still do this by programmatically starting the application context with plain java inside the step definitions class.
Though if you're dealing with micro services you may want to use something like minikube to spin up an actual environment because services don't stop/start instantly.
Though even this won't get you to all problems. At some point you'll need a real (enough) cluster with simulated sustained load and do some exploratory testing.
Fortunately this also means we're getting to the top of the testing piramid here. This means that while you won't be able to unit test your exact scenarios you can start to create rules (e.g. all http clients must use a connection pool and all connections must have a socket timeout and be confirmed working before use by the pool, or all thread pools must be able to finish all work within x-seconds, and applications should be given a grace period between sig-term and sig-kill) that are relatively easy to enforce by automatically inspecting code or providing well configured defaults.
So you may want to consider dividing your problem into a bit of boundary testing against the interfaces of each service, some exploratory testing and some policy writing and enforcement.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
