Spring Boot with 3 styles of tests – Example

If you want to see how to integration test, how to test rest api with mock, and  how to just mock , this page is for you

This example is a part of  Spring Boot with all examples

Full Code with Spring Boot 1.5.9.RELEASE:

13_spring-boot-test.zip

What you need for run this:

  • Maven 3
  • Java 8

What the code will do:

  • Show how to do integration test
  • Show how to test rest api with mock
  • Show how to just moock

Explain:

This is the file structure:

This service will just return “Count  result: [number]” when you call http://localhost:8080/api/count.

CountService.count() will just count and return int.

TestRestController.showCount() will just call CountService and add “Count result:”

Assume that our target test is TestRestCountroller. In other words, we want to test logic in TestRestController.

This is pom.xml

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

    <groupId>com.surasint.example</groupId>
    <artifactId>spring-boot-13</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

TestRestControllerTest1 will just try to test TestRestCountroller by mocking CountService. This is the code:

package com.surasint.example.web.api;

import com.surasint.example.service.CountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class TestRestControllerTest1 {

    @Mock
    private CountService mockCountService;

    @InjectMocks
    private TestRestController target;

    @Test
    public void test_count(){
        //given
        when(mockCountService.count()).thenReturn(100);

        //when
        String result = target.showCount();

        //then
        assertEquals("Count result:100", result);
    }
}

The keys are:

  • Line 13: @RunWith(MockitoJUnitRunner.class) to tell that we are going to use Mockito
  • Line 16: @Mock to create a mock object to mockCountService
  • Line 19: @InjectMock to inject mockCountService to “countService” in TestRestController object (the “target” object)

The problem with this test is that you cannot prove that you correctly map “@GetMapping(“/api/count”)” in TestRestController. But we have the solution in TestRestControllerTest2

TestRestControllerTest2 will mock CountService but it simulate a call test via rest api, let see the code:

package com.surasint.example.web.api;

import com.surasint.example.service.CountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(SpringRunner.class)
@WebMvcTest(TestRestController.class)
public class TestRestControllerTest2 {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private CountService mockCountService;

    @Test
    public void test_count() throws Exception {
        //given
        when(mockCountService.count()).thenReturn(100);

        //when
        MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get("/api/count")).andReturn();
        String result = mvcResult.getResponse().getContentAsString();

        //then
        assertEquals("Count result:100",result);
    }
}

The keys are:

  • Line 17 @RunWith(SpringRunner.class) to tell that it will use SpringRunner to test
  • Line 18 @WebMvcTest(TestRestController.class) to tell the target controller to test , this case is TestRestController
  • Line 30 We mock the result to return 100
  • Line 33 We use “mvc” to simulate a call the “/api/count”
  • Line 34 We get the result of the call

The most important thing about this style of test is that you can mock CountService to behave as we want.

TestRestControllerTest3 will do the full integration test, nothing will be mocked.

package com.surasint.example.web.api;

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.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestRestControllerTest3 {


    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void test_count() throws Exception {
        //given

        //when
        ResponseEntity<String> responseEntity =
                restTemplate.getForEntity("/api/count", String.class);
        String result = responseEntity.getBody();

        responseEntity =
                restTemplate.getForEntity("/api/count", String.class);
        String result2 = responseEntity.getBody();


        //then
        assertEquals("Count result:0",result);
        assertEquals("Count result:1",result2);
    }
}

The keys are:

  • Line 13 @RunWith(SpringRunner.class) to tell that it will use SpringRunner to test
  • Line 14 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) to tell that we will use random port wen start Spring Boot
  • Line 26-32 We use “restTemplate” to do http call and get the results

Try:

You can just run tests.

Note that TestRestControllerTest2 and 3 will start Spring Boot.