openshell 的个人博客

一天很长,但十年很短。

Open Source, Open Mind,
Open Sight, Open Future!
  menu
110 文章
5051 浏览
0 当前访客
ღゝ◡╹)ノ❤️

单元测试的正确打开方式

公司现在强制要求每个接口都必须写单元测试,所以是时候捡起并记录下以前的姿势了!

接口代码还是该咋写咋写,Controller、Service、Mapper编写略掉。

一、入坑方式

写法一:

package com.shengmiao.pinduoduo;

import com.shengmiao.master.controller.PinDuoDuoController;
import org.junit.Before;
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.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * <p>
 * 拼夕夕接口单元测试
 * </p>
 *
 * @author openshell
 * @since 2020-10-10
 */
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class PinDuoDuoControllerTest {
    private MockMvc mockMvc;

    //一定要使用@Autowired注入,否则该Controller下的Service无法被正确注入。
    @Autowired
    private PinDuoDuoController pinDuoDuoController;

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

    @Test
    public void testQueryPid() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/pdd/queryPid").accept(MediaType.APPLICATION_JSON_VALUE).param("status", "1"))
                .andDo(MockMvcResultHandlers.print());
    }
}

这样写需要自己注入Controller,注意一定要使用注解注入,不能去new。

写法二:

package com.shengmiao.pinduoduo;

import org.junit.Before;
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.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * <p>
 * 拼夕夕接口单元测试
 * </p>
 *
 * @author openshell
 * @since 2020-10-10
 */
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class PinDuoDuoControllerTest {
    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext webApplicationContext;


    @Before
    public void setup() {
        mockMvc= MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void testQueryPid() throws Exception {

mockMvc.perform(MockMvcRequestBuilders.get("/pdd/queryPid").accept(MediaType.APPLICATION_JSON_VALUE).param("status", "1"))
                .andDo(MockMvcResultHandlers.print());
    }
}

通过注入 WebApplicationContext,容器会自动帮我们加载所有Controller,就不需要手动一个一个Controller去注入了。

写法三:

package com.shengmiao.pinduoduo;

import com.shengmiao.master.controller.PinDuoDuoController;
import com.shengmiao.master.service.PinDuoDuoService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * <p>
 * 拼夕夕接口单元测试
 * </p>
 *
 * @author openshell
 * @since 2020-10-10
 */
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class PinDuoDuoControllerTest {
    private MockMvc mockMvc;

    @InjectMocks
    private PinDuoDuoController pinDuoDuoController;

    @Mock
    private PinDuoDuoService pinDuoDuoService;

    @Before
    public void setup() {
        //实例化方式一
        mockMvc = MockMvcBuilders.standaloneSetup(pinDuoDuoController).build();
    }

    @Test
    public void testQueryPid() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/pdd/queryPid").accept(MediaType.APPLICATION_JSON_VALUE).param("status", "1"))
                .andDo(MockMvcResultHandlers.print());
    }
}

这可能是比较专业的写法了trollface

关于MockMvc的用法:

  1. mockMvc.perform执行一个请求。
  2. MockMvcRequestBuilders.get("XXX")构造一个请求。
  3. ResultActions.param添加请求传值
  4. ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
  5. ResultActions.andExpect添加执行完成后的断言。
  6. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
  7. ResultActions.andReturn表示执行完成后返回相应的结果。

二、进一步理解

这些注解一直没仔细的理解过,如今乘机整理下。

@RunWith

@RunWith注解的作用是在括号内指定一个运行器,常用的运行器使用如下:

  • @RunWith(JUnit4.class)就是指用JUnit4来运行
  • @RunWith(SpringRunner.class)

值得一提的是,SpringRunner其实是SpringJUnit4ClassRunner的别名。

@code SpringRunner} is an <em>alias</em> for the {@link SpringJUnit4ClassRunner}.

在JUnit5中,已经主张使用**@ExtendWith**替换掉@RunWith。

关于升级迁移的文章:

  1. https://zhuanlan.zhihu.com/p/144763642
  2. https://www.baeldung.com/junit-5-runwith

@SpringBootTest

该注解主要作用是指定SpringBoot启动类和读取配置文件

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartUpApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerTest {

    /**
     * @LocalServerPort 提供了 @Value("${local.server.port}") 的代替
     */
    @LocalServerPort
    private int port;

    private URL base;

    @Autowired
    private TestRestTemplate restTemplate;

    @Before
    public void setUp() throws Exception {
        String url = String.format("http://localhost:%d/", port);
        System.out.println(String.format("port is : [%d]", port));
        this.base = new URL(url);
    }

    /**
     * 向"/test"地址发送请求,并打印返回结果
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {

        ResponseEntity<String> response = this.restTemplate.getForEntity(
                this.base.toString() + "/test", String.class, "");
        System.out.println(String.format("测试结果为:%s", response.getBody()));
    }

关于@Before、@BeforeClass、@Test、@After和@AfterClass注解的使用

@Before

在任意使用@Test注解标注的方法执行之前执行,注意 是每个@Test注解前就执行一次。
@BeforeClass
在每次测试类初始化时只执行一次
after的注解也是这区别。
使用示例:

public class JunitTest {
    @BeforeClass
    public static void beforeClass(){
        System.out.println("@BeforeClass 执行");
    }
    @Before
    public void test0(){
        System.out.println("@Before 执行");
    }

    @Test
    public void test2(){
        System.out.println("1111");
    }
    @Test
    public void test3(){
        System.out.println("2222");
    }

    @After
    public void testAfter(){
        System.out.println("@After 执行");
    }

    @AfterClass
    public static void testAfterClass(){
        System.out.println("@@AfterClass 执行");
    }
}

输出:

@BeforeClass 执行
@Before 执行
1111
@After 执行
@Before 执行
2222
@After 执行
@@AfterClass 执行

标题:单元测试的正确打开方式
作者:openshell
地址:http://solo.caiqz.cn/articles/2020/10/20/1603192917702.html