Testing Spring Boot with TestNG
I prefer to use TestNG for unit and integration testing, so the examples in this paper are based on TestNG.
And Spring & Spring Bot's official examples of Testing are mostly based on JUnit, so this article can also provide some useful help to TestNG enthusiasts.
Chapter List
- Chapter 0: Basic Concepts
- Chapter 1: Basic usage
- Chapter 2: Annotation Test Tool
- Chapter 3: Using Mockito
- Chapter 4: Testing Relational Databases
- Chapter 5: Testing Spring MVC
- Chapter 6: Test @Configuration TODO
- Chapter 7: Test @AutoConfiguration TODO
- Appendix I Spring Mock Objects
- Appendix II Spring Test Utils
Chapter 5: Testing Spring MVC
Spring Testing Framework Provided Spring MVC Test Framework It's easy to test Controller. Spring Boot also provides Auto-configured Spring MVC tests It further simplifies the configuration work needed for testing.
This chapter will illustrate how to test Spring MVC without Spring Boot and with Spring Boot, respectively.
Example 1: Spring
The key to testing Spring MVC is to use MockMvc objects, which allow us to test Controller behavior without starting the Servlet container.
source codeSpringMvc_1_Test.java:
@EnableWebMvc @WebAppConfiguration @ContextConfiguration(classes = { FooController.class, FooImpl.class }) public class SpringMvc_1_Test extends AbstractTestNGSpringContextTests { @Autowired private WebApplicationContext wac; private MockMvc mvc; @BeforeMethod public void prepareMockMvc() { this.mvc = webAppContextSetup(wac).build(); } @Test public void testController() throws Exception { this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("true")); } }
In this code, there are three main steps:
- Mark the test class as @WebAppConfiguration
- Building MockMvc through webAppContextSetup(wac).build()
- Using MockMvc to Judge the Result
Example 2: Spring + Mock
In Example 1, FooController uses an entity FooImpl's Bean. In fact, we can also provide a Foo's mock bean for testing, so that we can control the testing process more. If you don't know Mock yet, seeChapter 3: Using Mockito.
source codeSpringMvc_2_Test.java:
@EnableWebMvc @WebAppConfiguration @ContextConfiguration(classes = { FooController.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class SpringMvc_2_Test extends AbstractTestNGSpringContextTests { @Autowired private WebApplicationContext wac; @MockBean private Foo foo; private MockMvc mvc; @BeforeMethod public void prepareMockMvc() { this.mvc = webAppContextSetup(wac).build(); } @Test public void testController() throws Exception { when(foo.checkCodeDuplicate(anyString())).thenReturn(true); this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("true")); } }
Example 3: Spring Boot
Spring Boot provides @WebMvcTest to further simplify the testing of Spring MVC. We provide the Spring Boot version of the corresponding example 1.
source codeBootMvc_1_Test.java:
@WebMvcTest @ContextConfiguration(classes = { FooController.class, FooImpl.class }) public class BootMvc_1_Test extends AbstractTestNGSpringContextTests { @Autowired private MockMvc mvc; @Test public void testController() throws Exception { this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("true")); } }
Here, we don't need to build MockMvc ourselves, just use @Autowired injection. Is it convenient?
Example 4: Spring Boot + Mock
This is the Spring Book version corresponding to Example 2, source codeBootMvc_2_Test.java:
@WebMvcTest @ContextConfiguration(classes = { FooController.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class BootMvc_2_Test extends AbstractTestNGSpringContextTests { @Autowired private MockMvc mvc; @MockBean private Foo foo; @Test public void testController() throws Exception { when(foo.checkCodeDuplicate(anyString())).thenReturn(true); this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) .andDo(print()) .andExpect(status().isOk()) .andExpect(content().string("true")); } }