spring boot

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