Today we’re going to see how to create and more important, how to test your microservices with Spring Boot
To create REST services with Spring Boot is very easy but we usually forget to create the tests that assure us that the code does what is must to do
We’re going to create a service with Spring Boot and the tests with Cucumber
Creating the service with Spring Boot
First of all, we need to create the pom.xml
file
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>es.rubenjgarcia</groupId> <artifactId>spring-boot-examples</artifactId> <version>1.0-SNAPSHOT</version> <properties> <spring.boot.version>1.4.0.RELEASE</spring.boot.version> <java.version>1.8</java.version> <cucumber.version>1.2.4</cucumber.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-spring</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-junit</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> </project>
With these pom.xml
we have all that we need
Now we need to create the service. First the controller class
package es.rubenjgarcia.web; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController(value = "/") public class TestController { @RequestMapping public Response get() { return new Response("Hello World!"); } }
And then the response class
package es.rubenjgarcia.web; public class Response { private String message; public Response() {} public Response(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
We need to create another class in order to Spring Boot can start the service
package es.rubenjgarcia; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Now we can package our application and run it with the next commands
mvn clean package java -jar target/spring-boot-example-1.0-SNAPSHOT.jar
If we open http://localhost:8080
in our navigator we will see this
{"message":"Hello World!"}
Creating the tests classes with and without Cucumber
Now we have to create the test classes. We’ll create one with Cucumber and another without it. Let’s see first how to create the one without Cucumber
package es.rubenjgarcia.test; import es.rubenjgarcia.web.Response; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import java.util.HashMap; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class TestSuite { @Autowired private TestRestTemplate restTemplate; @Test public void test() { ResponseEntity<Response> response = this.restTemplate.getForEntity("/", Response.class, new HashMap<>()); Assert.assertEquals(HttpStatus.OK, response.getStatusCode()); response.getBody().getMessage().equals("Hello World!"); } }
Just mvn test
and you’ll see the result
Now let’s have some fun with Cucumber. In other post I’ll write more about this testing framework but today we’ll see a little example
First we need a file where we describe our features
Feature: Server reply Hello World! Scenario: Server reply Hello World! Given I call GET on / Then the response status is 200 And the response body must contain message with Hello World!
Our feature says that our server must reply Hello World!, let’s implement this test. First we need to implement the steps
package es.rubenjgarcia.test.cucumber; import cucumber.api.PendingException; import cucumber.api.java.en.And; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import es.rubenjgarcia.Application; import org.junit.Assert; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import java.util.HashMap; @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration public class TestSteps { @Autowired private TestRestTemplate restTemplate; private ResponseEntity<HashMap> response; @Given("I call GET on (.*)") public void iCallGetOn(String url) { this.response = this.restTemplate.getForEntity(url, HashMap.class, new HashMap<>()); } @Then("^the response status is (\\d+)$") public void theResponseStatusIs(int status) throws Throwable { Assert.assertEquals(status, response.getStatusCode().value()); } @And("^the response body must contain (.*) with (.*)$") public void theResponseBodyMustContainFieldWithValue(String field, String value) throws Throwable { Assert.assertTrue(response.getBody().containsKey(field)); Assert.assertEquals(value, response.getBody().get(field)); } }
And now we create the class that invoke the tests from our feature file
package es.rubenjgarcia.test.cucumber; import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import org.junit.runner.RunWith; @RunWith(Cucumber.class) @CucumberOptions(format = "pretty", features = "src/test/resources/cucumber") public class CucumberTest { }
Now if you execute mvn test
you’ll see all the tests passing, the previous tests and the Cucumber tests
You can fork this examples from Github