iangilham.com

By on , modified

code spring webapps

Note: All the code here is licensed under the Apache License version 2.0.

Update: The information and in this post has been adapted back into the Getting Started Guide. I recommend reading the guide, as it is more complete and easier to follow.

For the last few weeks I’ve been using Spring Boot to build a REST application for an internal service. I started with the Getting Started Guide and built it up from there. Once I got the application in the guide working, I started to look at how to write tests for such an application to drive further development.

I started by forking the guide’s source code sample on GitHub and creating the requisite test source directories following the usual Maven conventions.

For reference, the web endpoint in the sample application can be found in class HelloController:

package hello;

// imports

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}

The application just returns a document containing a plain string when clients request the /hello endpoint.

Unit testing

Getting set up for testing requires a dependency to be added to the pom.xml as follows:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

This gives us JUnit and some other stuff useful for working with Spring applications.

For the test itself, we can hit the web endpoint defined by the HelloController class without spinning up a Servlet container and the rest of the application. This allows us to mock out any dependencies not under test (none for the example application) and verify the behaviour of the controller.

package hello;

// imports

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@ContextConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class HelloControllerTest {
    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(
                new HelloController())
            .build();
    }

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders
            .get("/hello")
            .accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(
                is("Greetings from Spring Boot!")));
    }
}

The annotations on the test class orchestrate the setup of the application context here. @SpringApplicationConfiguration tells Spring to load the application context from the application’s normal entry point, building any required beans etc. @WebAppConfiguration sets up some machinery required to enable testing of web endpoints. @ContextConfiguration adds a @Configuration class to the context to enable some more beans, in this case to provide a mock Java Servlet container.

The test itself just uses Spring’s testing library to call GET /hello and verifies that the returned value is what was expected. There are more complex ways to assert on the result of hitting an endpoint but this is sufficient for the sample application.

Integration testing

Maven does not enable the integration testing build phase by default so we have to add the failsafe plugin to make it work:

<plugin>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.18</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
</plugin>

With the failsafe plugin in place, maven will run our integration test.

package hello;

// imports

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest({"server.port=9090"})
public class HelloControllerIT {
    private String base;
    private RestTemplate template;

    @Before
    public void setUp() throws Exception {
        this.base = "http://localhost:9090/hello";
        template = new TestRestTemplate();
    }

    @Test
    public void getHello() throws Exception {
        ResponseEntity<String> response =
            template.getForEntity(base, String.class);
        assertThat(response.getBody(),
            is("Greetings from Spring Boot!"));
    }
}

The integration test is very similar to the unit test but it has a different runtime lifecycle. This time, the application will be run in an embedded Servlet container with all its dependencies before the test code is called. The @IntegrationTest annotation tells Spring to run the full application stack.

Integration testing like this is unnecessary for this simple application but it can be useful when a black-box test of the entire application is required. I have found this method particularly useful for tests that touch a database tier, as it is possible to customise the beans in play for each integration test class, enabling database compatibility testing where more than one vendor is supported, or enabling the tests to run against a local instance of a distributed NoSQL database.

My code can be found on GitHub.