Testing web layer in Spring Boot using WebMvcTest

Upasana | November 26, 2019 | 2 min read | 568 views


In this tutorial we will write a Spring Boot based test class that tests the web layer of our application without loading the full Spring context. These tests are often used to verify single or multiple containers

Perquisites

  1. Java 8 or Java 11

  2. Gradle 5+

  3. Spring boot 2.2.1

  4. Junit 5

Using @SpringBootTest in tests will load the full Spring application context but without the server. But we can narrow down the testing scope to just web layer by using @WebMvcTest annotation.

Using @WebMvcTest, Spring Boot will only instantiate the web layer, not the whole context. That means:

Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not @Component, @Service or @Repository beans

We can ask for just one controller to be initialized using @WebMvcTest(GreetingController.class)

Let’s create a simple Spring Boot application with a controller and service layer

We can use https://start.spring.io to create a sample application.

spring initlaizer webmvctest
Using Spring Starter to create project

We are using gradle build system to manage the build dependencies.

build.gradle
plugins {
	id 'org.springframework.boot' version '2.2.1.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.shunya.tutorial'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

We will create a simple controller that has a /greeting endpoint, which invokes a service to capture the greeting response.

GreetingController.java
@Controller
public class GreetingController {

    private final GreetingService service;

    public GreetingController(GreetingService service) {
        this.service = service;
    }

    @RequestMapping("/greeting")
    @ResponseBody
    public String greeting() {
        return service.greet();
    }

}

Here is our dummy GreetingService which just returns a mocked response.

GreetingService.java
@Service
public class GreetingService {
    public String greet() {
        return "Hello World";
    }
}

Now lets write a simple class that will do the web layer testing for us:

WebMockTest.java
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.when;
import static org.springframework.test.*;

@WebMvcTest(GreetingController.class)
public class WebMockTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private GreetingService service;

    @Test
    public void greetingShouldReturnMessageFromService() throws Exception {
        when(service.greet()).thenReturn("Hello Mock");
        this.mockMvc
                .perform(get("/greeting"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello Mock")));
    }
}

The above test will instantiate GreetingController but not the GreetingService component.

We can see that WebMvcTest is often used along with @MockBean to create any collaborators required by our @Controller beans.


Top articles in this category:
  1. Spring Boot 2.0 Reactive Web Performance Metrics
  2. Slack Webhook Message from Spring Boot
  3. Feign RequestInterceptor in Spring Boot
  4. Sendgrid Dynamic Templates with Spring Boot
  5. Basic Auth Security in Spring Boot 2
  6. Redis rate limiter in Spring Boot
  7. SendGrid emails in Spring Boot

Recommended books for interview preparation:

Find more on this topic: