一、IDEA自动生成测试类

1.安装插件JUnitGenerator V2.0

File---->Settings---->Browse Repositories

idea 检查springboot 版本 idea springboot test_MockMvc

2.安装好后把JUnit和JUnitGenerator V2.0一起勾上

idea 检查springboot 版本 idea springboot test_单元测试_02

3.配置插件

把package上test.前缀去掉

idea 检查springboot 版本 idea springboot test_spring_03

配置测试类生成的地址

${SOURCEPATH}/../../test/java/${PACKAGE}/${FILENAME}

idea 检查springboot 版本 idea springboot test_MockMvc_04

4.对着要生成的类

idea 检查springboot 版本 idea springboot test_MockMvc_05

idea 检查springboot 版本 idea springboot test_spring_06

idea 检查springboot 版本 idea springboot test_IDEA自动生成测试类_07

二、测试service类

@SpringBootTest会加载spring的上下文,这样可以使用@Autowired注入Bean

package com.sid.service.impl;

import com.sid.service.UserService;
import org.junit.After;
import org.junit.Assert;
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.test.context.junit4.SpringRunner;
import static org.hamcrest.CoreMatchers.*;

import static org.junit.Assert.*;

/** 
* @Description: Service层单元测试  启动了服务
* @Param:  
* @return:  
* @Author: Sid
* @Date: 2018-10-24 13:53
* @since: 1.0
*/ 
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
    
    
    /** 
    * @Description: 直接测试service  用IOC注入进来  这样run的时候会先启动springboot项目 
    * @Param:  
    * @return:  
    * @Author: Sid
    * @Date: 2018-10-24 13:42
    * @since: 1.0
    */ 
    @Autowired
    UserService userService;

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void set() {
        userService.set("sid:test:string:key:","lalala");
        String value = userService.get("sid:test:string:key:");
        Assert.assertEquals("lalala",value);
        //可以只使用 assertThat 一个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想
        //引入import static org.hamcrest.CoreMatchers.*;
        Assert.assertThat(value,is("lalala"));
    }

}

三、测试Controller类

有3种方法,1可以跟上面一样用@Autowired把Controller注入进来然后调用Controller的方法。参照上方,这里不介绍。

2.用MockMvc模拟的HTTP请求

package com.sid.controller;

import org.junit.After;
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.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
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;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.CoreMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;


/** 
* @Description: Controller层单元测试 注入Spring 上下文的环境到 MockMvc 中  测试时会启动项目
* @Param:  
* @return:  
* @Author: Sid
* @Date: 2018-10-24 13:45
* @since: 1.0
*/ 
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

    /** 
    * @Description: 使用MockMvc 模拟HTTP请求 来测试controller
     * MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,
     * 这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
     *
     * // 模拟MVC对象,通过MockMvcBuilders.webAppContextSetup(this.wac).build()初始化。
    * @Param:  
    * @return:  
    * @Author: Sid
    * @Date: 2018-10-24 13:43
    * @since: 1.0
    */ 

    private MockMvc mvc;

    @Autowired
    private WebApplicationContext wac; // 注入WebApplicationContext

    /** 
    * @Description: 直接注入userController 然后调用userController的方法来测试
    * @Param:  
    * @return:  
    * @Author: Sid
    * @Date: 2018-10-24 13:44
    * @since: 1.0
    */ 
//    @Autowired
//    private UserController userController;

    @Before
    public void setUp() throws Exception {
        //使用 WebApplicationContext 构建 MockMvc
        this.mvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void addUser() throws Exception {
        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user/add")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //form表单格式传参
                .param("id", "4")
                .param("name", "junit test")
                .param("password", "111")
                .param("mobilePhone", "18523980000")
                .characterEncoding("utf-8")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ResultActions result = mvc.perform(requestBuilder);

        MvcResult mvcResult = result.andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();// 返回执行请求的结果

        System.out.println("response------------------:"+mvcResult.getResponse().getContentAsString());
    }

    @Test
    public void deleteUser() throws Exception {
        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user/delete")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //form表单格式传参
                .param("mobilePhone", "18523980000")
                .characterEncoding("utf-8")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ResultActions result = mvc.perform(requestBuilder);

        MvcResult mvcResult = result.andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$").value(1))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();// 返回执行请求的结果

        System.out.println("response------------------:"+mvcResult.getResponse().getContentAsString());
    }

    @Test
    public void updateUser() throws Exception {
        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user/update")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //form表单格式传参
                .param("id", "1")
                .param("name", "名字")
                .param("mobilePhone", "185")
                .param("password", "5")
                .characterEncoding("utf-8")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ResultActions result = mvc.perform(requestBuilder);

        MvcResult mvcResult = result.andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();// 返回执行请求的结果

        System.out.println("response------------------:"+mvcResult.getResponse().getContentAsString());
    }

    @Test
    public void selectAll() throws Exception {

        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user/selectAll")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //form表单格式传参
                .param("pageNum", "1")
                .param("pageSize", "5")
                .characterEncoding("utf-8")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ResultActions result = mvc.perform(requestBuilder);

        MvcResult mvcResult = result.andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(jsonPath("list").exists())
                .andExpect(jsonPath("$.list", notNullValue()))
                .andExpect(jsonPath("$.list[0].id", is(1)))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();// 返回执行请求的结果

        System.out.println("response------------------:"+mvcResult.getResponse().getContentAsString());


    }

    @Test
    public void getUserByMobilePhone() throws Exception {

        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/user/getUserByMobilePhone")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //form表单格式传参
                .param("mobilePhone", "18523980000")
                .characterEncoding("utf-8")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE);

        ResultActions result = mvc.perform(requestBuilder);

        MvcResult mvcResult = result.andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(jsonPath("$", notNullValue()))
                .andExpect(jsonPath("$.name", is("junit test")))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();// 返回执行请求的结果

        System.out.println("response------------------:"+mvcResult.getResponse().getContentAsString());


    }

}

3.用TestRestTemplate 完整HTTP请求测试

package com.sid.controller;

import com.github.pagehelper.PageInfo;
import com.sid.model.User;
import org.hamcrest.Matchers;
import org.junit.Assert;
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.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;


/**
 * @program: springboot
 * @description:
 * @author: Sid
 * @date: 2018-11-15 10:52
 * @since: 1.0
 **/
@RunWith(SpringRunner.class)
//开启一个随机的可用端口
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestRestTemplateTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void listAll() {
        String url  = "/user/selectAll";
        // 封装入参数,不要替换为Map与HashMap,否则参数无法传递
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("pageNum", 1);
        paramMap.add("pageSize", 5);
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(paramMap,headers);


        // 1、使用postForObject请求接口
        PageInfo<User> result = restTemplate.postForObject(url, paramMap, PageInfo.class);
        System.out.println("result1==================" + result.getList());

        // 2、使用postForEntity请求接口
        ResponseEntity<PageInfo> response2 = restTemplate.postForEntity(url, httpEntity, PageInfo.class);
        System.out.println("result2====================" + response2.getBody().getList());

        // 3、使用exchange请求接口
        //设置接受参数的类型 只有用exchange+ParameterizedTypeReference接收到的pageInfo中的List是user类,上面两种方式接收到的都是个LinkedList
        ParameterizedTypeReference<PageInfo<User>> type = new ParameterizedTypeReference<PageInfo<User>>() {};
        ResponseEntity<PageInfo<User>> response3 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, type);
        System.out.println("result3====================" + response3.getBody().getList());
        Assert.assertThat(response3.getBody().getList(), Matchers.notNullValue());
    }

}

四、常用注解介绍

@Before:初始化方法
@After:释放资源
@Test:测试方法(包括:期望异常和超时时间)

@Test(timeout = 1000),超时会失败。

@Test(expected = NullPointerException.class) 希望抛出空指针异常

@Ignore:忽略的测试方法
@BeforeClass:针对所有测试,只执行一次,且必须为static void
@AfterClass:针对所有测试,只执行一次,且必须为static void
@RunWith:可以更改测试运行器
执行顺序:

@BeforeClass ==> @Before ==> @Test ==> @After ==> @AfterClass 

 

@SpringBootTest会加载spring的上下文,这样可以使用@Autowired注入Bean

 

五、参数化测试

1.使用parameterized入参

每一组参数执行一次test的入参

package com.sid.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.List;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

/**
 * @program: springboot
 * @description: 参数化测试
 * @author: Sid
 * @date: 2018-11-15 10:33
 * @since: 1.0
 **/

//1.需要使用Parameterized运行器
@RunWith(Parameterized.class)
public class ParameterTest {
    // 2.声明变量存放预期值和测试数据
    private String firstName;
    private String lastName;

    //3.声明一个返回值 为Collection的公共静态方法,并使用@Parameters进行修饰
    @Parameterized.Parameters //
    public static List<Object[]> param() {
        // 两个测试用例
        return Arrays.asList(new Object[][]{{"Sid", "Lee"}, {"Tom", "Cat"}});
    }

    //4.为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值
    public ParameterTest(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    // 5. 进行测试,发现它会将所有的测试用例测试一遍
    @Test
    public void test() {
        String name = firstName + " " + lastName;
        assertThat("Sid Lee", is(name));
    }

}

 

idea 检查springboot 版本 idea springboot test_spring boot_08

2.使用Theories入参

提供一组参数的排列组合值作为待测方法的输入参数。

使用Theories这个Runner的时候,待测方法可以拥有输入参数,而这在其它的Runner中的测试方法是不行的。

package com.sid.controller;

import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

/**
 * @program: springboot
 * @description:
 * @author: Sid
 * @date: 2018-11-15 11:41
 * @since: 1.0
 **/
@RunWith(Theories.class)
public class TestTheories {
    @DataPoint
    public static String nameValue1 = "Sid";
    @DataPoint
    public static String nameValue2 = "Pencil";
    @DataPoint
    public static int ageValue1 = 27;
    @DataPoint
    public static int ageValue2 = 18;

    //也可以用这种方式提供参数
//    @DataPoints
//    public static String[] names = {"Sid", "Pencil"};
//    @DataPoints
//    public static int[] ages = {27, 18};

    @Theory
    public void testMethod(String name, int age){
        System.out.println(String.format("%s's age is %s", name, age));
    }
}

结果: 

idea 检查springboot 版本 idea springboot test_spring boot_09