I have read many of the guides about working with Spring Boot and RESTful services, and many of them contain information about running unit tests, most notably "Building an Application with Spring Boot". However, I haven't seen anything that gives an example on how to unit test a Spring Boot application that consumes/depends on other Spring Boot applications, as is common in cloud micro-services architecture. So, for example, we have the following Spring Boot services:
ServiceMediator, Adapter1, Adapter2
ServiceMediator calls Adapter1 or Adapter2, depending on the input.
Is there a way to start up the Spring Boot services Adapter1 and Adapter2 before starting and testing the ServiceMediator in a Spring JUnit test?
The process-exec-maven-plugin could be helpful as allows to start multiple java processes at pre-integration-test phase (as standard Spring Boot apps), and it automatically takes care of shutting them down in the post-integration-test phase.
NOTE: The integration test should be run at the integration-test phase for that the maven-failsafe-plugin should be configured with spring-boot-maven-plugin see. Then to run our integration tests verify or higher maven Lifecycle should be targeted, because the integration-test phase is in fact located between package and verify Lifecycles see Default Lifecycles.
The following maven (pom.xml) configuration worked for me:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.5.RELEASE</version>
<executions>
<execution>
<id>pre-integration-test</id>
<goals>
<goal>start</goal>
</goals>
<configuration>
<skip>${integration-tests.skip}</skip>
</configuration>
</execution>
<execution>
<id>post-integration-test</id>
<goals>
<goal>stop</goal>
</goals>
<configuration>
<skip>${integration-tests.skip}</skip>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<skip>${integration-tests.skip}</skip>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.bazaarvoice.maven.plugins</groupId>
<artifactId>process-exec-maven-plugin</artifactId>
<version>0.7</version>
<executions>
<execution>
<id>switchboard-process</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<name>accounts-service</name>
<workingDir>/../../micro-service</workingDir>
<waitForInterrupt>false</waitForInterrupt>
<arguments>
<argument>java</argument>
<argument>-jar</argument>
<argument>${basedir}/../micro-service/target/micro-service-${project.version}-exec.jar</argument>
</arguments>
</configuration>
</execution>
<!--Stop all processes in reverse order-->
<execution>
<id>stop-all</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop-all</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Having an Integration Test class (WebServerIT) in test.java folder:
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = WebServerApp.class)
@WebIntegrationTest("server.port:0")
public class WebServerIT {
@Autowired
private WebApplicationContext webServerAppContext;
private MockMvc webServerMockMvc;
@Before
public void setUp() {
System.out.println("the test is set up");
webServerMockMvc = MockMvcBuilders.webAppContextSetup(webServerAppContext).build();
}
/**
* This test calls the WebServer's endpoint (/accounts/123456789) which in turn calls the micro-service rest api
* which is started using the process-exec-maven-plugin, otherwise the test would fail.
*/
@Test
public void testWebServerInteractionWithMicroService() throws Exception {
this.webServerMockMvc.perform(get("/accounts/123456789"))
.andExpect(status().isOk());
}
}
package controller;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.net.URL;
import mediator.CLPApplication;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CLPApplication.class)
@WebAppConfiguration
@IntegrationTest()
public class ControllerTest {
@Value("${adapter.dependency.jar.location}")
private String adapterDependencyJarLocation;
@Value("${adapter.dependency.jar.name}")
private String adapterDependencyJarName;
@Value("${adapter.url}")
private String adapterURL;
@Value("${mediator.url}")
private String mediatorURL;
private URL mediator;
private URL adapter;
private RestTemplate template;
Process process = null;
@Before
public void setUp() throws Exception {
adapter = new URL(adapterURL);
template = new TestRestTemplate();
//
// Start the Atomic adapter
//
System.out.println(adapterDependencyJarLocation);
System.out.println("Starting Adapter");
try {
process = new ProcessBuilder("java", "-jar", adapterDependencyJarName)
.directory(new File(adapterDependencyJarLocation)).start();
// Try connecting 5 times with a 5 second pause between each
// to see if it started.
Thread.sleep(5000);
for(int i = 0; i <= 5; i++) {
try{
System.out.println("Testing to see if Adapter is up");
template.getForEntity(adapter.toString(), String.class);
System.out.println("Adapter Started");
break;
}
catch(RestClientException rce){
System.out.println("It's not up yet");
}
Thread.sleep(5000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testMediator() throws Exception {
mediator = new URL(mediatorURL);
System.out.println("Calling Mediator");
ResponseEntity<String> response = template.getForEntity(mediator.toString(), String.class);
System.out.println(response.getBody());
// Getting back JSON, so check to see if it starts with an open bracket
assertThat(response.getBody(), Matchers.startsWith("{"));
}
@After
public void tearDown() {
if(process != null) {
process.destroy();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With