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.)