spring Boot测试的最佳实践和测试架构的启发(JUnit4和mockito,包括MockMvc)

1. JUnit4

请看这里:Junit4 简单教程 和 Spring Boot Junit单元测试

2. mockito(包括MockMvc)

请看这里:强大的Mockito测试框架 和 Junit学习笔记之五:MockMVC

3. 最佳实践

Jar包版本说明:

  • junit:junit:4.12,
  • org.mockito:mockito-core:1.10.19

3.1 JUnit4 最佳实践

请注意观察父子类之间各个函数执行的顺序:

  1. 从外到内:BeforeClass>Before>Test;
  2. 不同层次的同类型函数,父类的总在子类外面;
  3. 同一个文件中的同类型函数的执行顺序是随机的。
public class MyTest1GrandFather {

    @BeforeClass
    public static void grandFatherBeforeClass() {
        System.out.println("MyTest1GrandFather.beforeClass");
    }

    @AfterClass
    public static void grandFatherAfterClass() {
        System.out.println("MyTest1GrandFather.afterClass");
    }

}
public class MyTest1Father extends MyTest1GrandFather {

    @BeforeClass
    public static void fatherBeforeClass() {
        System.out.println("MyTest1Father.beforeClass");
    }

    @Before
    public void fatherBefore1() {
        System.out.println("MyTest1Father.before1");
    }
    @Before
    public void fatherBefore2() {
        System.out.println("MyTest1Father.before2");
    }

    @After
    public void fatherAfter1() {
        System.out.println("MyTest1Father.after1");
    }
    @After
    public void fatherAfter2() {
        System.out.println("MyTest1Father.after2");
    }

    @AfterClass
    public static void fatherAfterClass() {
        System.out.println("MyTest1Father.afterClass");
    }

}
package hib;

import org.junit.*;


public class MyTest1 extends MyTest1Father {

    @BeforeClass
    public static void beforeClass() {
        System.out.println("MyTest1.beforeClass");
    }


    @Before
    public void before2() {
        System.out.println("MyTest1.before2");
    }
    @Before
    public void before1() {
        System.out.println("MyTest1.before1");
    }
    @Before
    public void before3() {
        System.out.println("MyTest1.before3");
    }


    @Test
    public void test2() throws Exception {
        System.out.println("MyTest1.test2");
    }
    @Test
    public void test1() throws Exception {
        System.out.println("MyTest1.test1");
    }
    @Ignore
    @Test
    public void test3() throws Exception {
        System.out.println("MyTest1.test3");
    }


    @After
    public void after1() {
        System.out.println("MyTest1.after1");
    }
    @After
    public void after2() {
        System.out.println("MyTest1.after2");
    }


    @AfterClass
    public static void afterClass() {
        System.out.println("MyTest1.afterClass");
    }

}
MyTest1的测试结果:

MyTest1GrandFather.beforeClass
MyTest1Father.beforeClass
MyTest1.beforeClass
MyTest1Father.before2
MyTest1Father.before1
MyTest1.before3
MyTest1.before2
MyTest1.before1
MyTest1.test1
MyTest1.after1
MyTest1.after2
MyTest1Father.after1
MyTest1Father.after2
MyTest1Father.before2
MyTest1Father.before1
MyTest1.before3
MyTest1.before2
MyTest1.before1
MyTest1.test2
MyTest1.after1
MyTest1.after2
MyTest1Father.after1
MyTest1Father.after2

Test ignored.MyTest1.afterClass
MyTest1Father.afterClass
MyTest1GrandFather.afterClass

说明:

1. 从现象上看

@Before注解是按照函数名的字典序的倒序执行的,@After注解是按照函数名的字典序的正序执行的,但是并没有文档做出说明,所以不能依赖此顺序。

2. 分析源码可以看出(见下面的3张图片)

Test类里的函数先会进行一次排序,排序是先根据hashCode排;如果hashCode一样,再用函数名按照字典序升序排;如果函数名也一样,就用函数toString()的结果排。

上面的排序完成后,Before和BeforeClass注解的会逐个插入到执行队列的前面,其它的逐个插入到执行队列的后面。

但是文档中并没有给出承诺,所以还是不能依赖此顺序。

spring Boot测试的最佳实践和测试架构的启发(JUnit4和mockito,包括MockMvc)_第1张图片

spring Boot测试的最佳实践和测试架构的启发(JUnit4和mockito,包括MockMvc)_第2张图片

spring Boot测试的最佳实践和测试架构的启发(JUnit4和mockito,包括MockMvc)_第3张图片

 

 

3.2 spring boot + JUnit4 最佳实践

package test;

import org.junit.runner.RunWith;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

@ActiveProfiles({"test"})
@RunWith(SpringRunner.class)
@SpringBootTest(classes = VotingApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BaseTest {

    @LocalServerPort
    protected int port;

}
package test;

import hib.entity.StudentDao;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;


public class MyTest2 extends BaseTest {

    @Autowired
    StudentDao studentDao;

    @Test
    public void test() throws Exception {
        System.out.println(studentDao.count()); //1
    }
}

参考:

[1] 使用@SpringBootTest注解进行单元测试

[2] Spring boot 单元测试

 

3.3 spring boot + JUnit4 + mockito 最佳实践

package test;

import org.junit.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;

import static org.mockito.Mockito.when;


public class MyTest3 extends BaseTest {

    @Autowired
    service s1;

    @Mock
    service s2;

    @Test
    public void test() throws Exception {
        System.out.println(s1.st1()); //hello
        System.out.println(s2.st1()); //null
        when(s2.st1()).thenReturn("123");
        System.out.println(s2.st1()); //123
    }

}

3.4 spring boot + JUnit4 + mockito + MockMvc 最佳实践

package hib;

import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;


public class MyTest4 extends ControllerTest {


    @Autowired      //负责找到controller
    @InjectMocks    //负责把MyTest4中所有有@Mock注解的对象注入到controller中
    controller c1;  //这里2个注释都要有,缺一不可

    @Mock 
    service s1;     //模拟service


    @Test
    public void test() throws Exception {

        //1.模拟service中st1()的行为,本来应该返回hello,模拟后返回123
        when(s1.st1()).thenReturn("123");

        //2.模拟请求
        MvcResult res = mvc.perform
                (
                    get("/test/t1")                                     //请求路径
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED) //请求类型
                    .param("args", "root")                              //请求参数
                )
                .andExpect(status().isOk())             //预测返回状态
                .andExpect(content().string("123"))     //预测返回结果1
                .andDo(print())                         //执行回调函数
                .andReturn();                           //返回MvcResult对象

        //3.断言返回结果2,和上面预测返回结果1处的作用相同
        assertEquals("123", res.getResponse().getContentAsString()); 
    }

}

4. 测试架构的启发

分层设计测试架构,提取公共的初始化部分和测试业务。参见第3节-最佳实践。

 

 

你可能感兴趣的:(测试,Java)