单元测试有助于验证程序的执行逻辑是否正确。controller层的单元测试,已经和接口测试很类似了。执行单元测试以前,需要添加测试依赖。

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

1. 生成一个单元测试基础类

package com.demo.order.controller;

import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

/**
 * 
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class BaseSpringBootTest {

    protected Logger logger = LoggerFactory.getLogger(BaseSpringBootTest.class);

    @Before
    public void init() {
        logger.info("开始测试...");
    }

    @After
    public void after() {
        logger.info("测试结束...");
    }
}

注意需要添加的注解。

2. 没有参数的单元测试

package com.demo.order.controller;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

/**
 * 
 */
public class OrderControllerTest extends BaseSpringBootTest {

    @Autowired
    private OrderController orderController;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(orderController).build();
    }

    @Test
    public void demo() throws Exception {
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/order/add"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        logger.info(mvcResult.getResponse().getContentAsString());
    }

}

执行单元测试,可以看到如下输出:

2022-01-08 17:06:43.936  INFO 24860 --- [           main] c.f.order.controller.BaseSpringBootTest  : 开始测试...
2022-01-08 17:06:43.962  INFO 24860 --- [           main] o.s.mock.web.MockServletContext          : Initializing Spring TestDispatcherServlet ''
2022-01-08 17:06:43.962  INFO 24860 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
2022-01-08 17:06:43.962  INFO 24860 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 0 ms
下单成功

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /order/add
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.demo.order.controller.OrderController
           Method = com.demo.order.controller.OrderController#add()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"text/plain;charset=ISO-8859-1", Content-Length:"12"]
     Content type = text/plain;charset=ISO-8859-1
             Body = Hello world!
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-01-08 17:06:44.051  INFO 24860 --- [           main] c.f.order.controller.BaseSpringBootTest  : Hello world!
2022-01-08 17:06:44.060  INFO 24860 --- [           main] c.f.order.controller.BaseSpringBootTest  : 测试结束...
2022-01-08 17:06:44.081  INFO 24860 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

3. 有参数的单元测试(json格式)

MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/secret/encryption")
                                     .accept(MediaType.APPLICATION_JSON).param("originContent", "15221365094"))
                                     .andExpect(MockMvcResultMatchers.status().isOk())
                                     .andDo(MockMvcResultHandlers.print())
                                     .andReturn();

这里的.accept(MediaType.APPLICATION_JSON).param("originContent", "15221365094"))就是增加json格式的参数。

4. 有参数的单元测试(对象转json)

MobileTelephoneRequest request = new MobileTelephoneRequest();
        request.setCustomerId("RL20180304000099");
        request.setMobileTelephone("18883270484");
        request.setCcfMobileTelephone("13904108866");
        
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/customerEncryption/save")
                                     .contentType(MediaType.APPLICATION_JSON)
                                     .content(JSONObject.toJSONString(request)))
                                     .andExpect(MockMvcResultMatchers.status().isOk())
                                     .andDo(MockMvcResultHandlers.print())
                                     .andReturn();

5. 一次性执行多个单元测试

package com.demo.order.controller;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
 * 打包测试
 * 配置测试类,一次性执行所有配置的测试类
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({OrderControllerTest.class,DemoControllerTest.class})
public class SuiteExecuteTests {
    // 不用写代码,只需要注解即可(在SuiteClasses中配置测试类)
}

单元测试可以将程序启动和测试结合起来,一般用于开发和测试环境。这样就避免了必须借助于工具或者自己写代码构造http请求的繁琐。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐