Mockito使用

本文混乱,待编辑优化……

本文参考 使用Mockito进行单元测试【1】——mock and verifyMockito 中文文档 ( 2.0.26 beta )

Mockito的使用包括

  • 1.Mockito mock---mock一个接口或类
  • 2.Mockito stubbing---打桩功能stubbing  使方法调用返回期望的值 
  • 3.Mockito verfiry---verify验证,验证mock的接口中的方法是否被调用,被调用次数
  • 4.Mockito argument matchers---在stubbing或者verify时,模拟传入的参数 
  • 5.Mockito.spy---Mock同一个类中的函数调用

1.Mockito---mock一个接口或类

创建一个类或接口的mock实例 是指这个mock实例拥有这个类或接口的所有方法,并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。

a.创建一个类或接口的mock实例---使用mock()方法

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.List;
import org.junit.jupiter.api.Test;

public class MockitoTest {
	@Test
	public void test() throws Exception {

		// 使用mock()方法 创建List接口的mock实例
		List<String> list = mock(List.class);
		
		list.add("xx");
		
		verify(list, times(1)).add("xx");
		verify(list, times(1)).add("y");
	}

}


b.创建一个类或接口的mock实例---使用@MockBean注解

在SpringBoot Test中,@MockBean注解 可以创建接口的mock实例,被用来添加 mock 对象到 Spring ApplicationContext

	//@MockBean 创建接口的mock实例
	//这个studentService实例就拥有StudentService接口的所有方法
	//并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。
	@MockBean
	private StudentService studentService;

2.Mockito---打桩功能stubbing  使方法调用返回期望的值 

打桩是一个形象的说法,就是把所需的测试数据塞进对象中,适用于基于状态的(state-based)测试,关注的是输入和输出。
Mockito 中 when(xxxx).thenReturn(yyyy):在when中定义对象方法和参数(输入),然后在 thenReturn 中指定结果(输出)。此过程称为 Stub 打桩。

比如以下情况:controller 依赖service,调用service的findAll()方法获取Student的集合并且对获取到的Student集合 进行下一步处理时(把所有Student的name打印到控制台),这时候就可以用mockito的打桩功能,给service的findAll()方法返回的是个固定值。然后在controller中继续处理。

一旦这个方法被 stub 了,就会一直返回这个 stub 的值。

		// mock data
		List<Student> students = new ArrayList<Student>();
		Student student1 = new Student();
		student1.setName("xxx");
		students.add(student1);

		Student student2 = new Student();
		student2.setName("yyy");
		students.add(student2);

		// 打桩 stub
		Mockito.when(studentService.findAll()).thenReturn(students);

3.Mockito---verify验证

验证:

  • 有返回结果的方法:可以通过打桩这些方法,然后调用方法,判断返回值是不是 用来打桩的值;也可以直接判断这个方法有没有被调用过。
  • 没有返回结果,void类型的方法:只能通过是否被触发执行来做相应的判断。对于 void 返回值的方法一般可不用去 mock 它,只需用  verify() 去验证。参考Mockito 如何 mock 返回值为 void 的方法

Mockito提供了verify()方法来验证这个mock实例的某个方法是否被调用,调用了几次,是否带有参数以及带有何种参数等。

		// 验证方法执行过一次
		verify(studentService, times(1)).findAll();
		//最少调用一次
		verify(studentService,atLeastOnce()).findAll();
		//最多调用一次
		verify(studentService,atMostOnce()).findAll();
		//从没调用过
		verify(studentService,never()).findAll();

4.Mockito---模拟传入的参数 argument matchers 

参考 JUnit + Mockito 单元测试(二)

有时候要stubbing或者verify的方法有参数,但是我不关心输入的具体内容。只是完成打桩或者验证。这时候就可以用Mockito提供的argument matchers 机制,
例如 anyString() 匹配任何 String 参数,anyInt() 匹配任何 int 参数,anySet() 匹配任何 Set,any() 则意味着参数为任意值。

模拟参数可以用在两种情况下:打桩时模拟传入的参数 验证时模拟传入的参数 

package com.example.junittestdemo;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
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.result.MockMvcResultMatchers;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@AutoConfigureMockMvc
@SpringBootTest(classes = StudentController.class)
public class StudentControllerTest {

	@Autowired
	private MockMvc mockMvc;

	@MockBean
	private StudentService studentService;

	@Test
	public void testfindById() throws Exception {

		// mock data
		Student student = new Student();
		student.setName("mm");

		// stubbing 传入参数any()
		Mockito.when(studentService.findById(any())).thenReturn(student);
		
		// stubbing 传入参数anyLong()
		Mockito.when(studentService.findById(anyLong())).thenReturn(student);
		
		// stubbing 传入参数90
		Mockito.when(studentService.findById(new Long(10))).thenReturn(student);
		

		// 执行一个get请求
		mockMvc.perform(MockMvcRequestBuilders.post("/student/find")
				.contentType(MediaType.APPLICATION_JSON_VALUE)
				.content("90"))
				.andExpect(MockMvcResultMatchers.status().isOk())
				.andDo(MockMvcResultHandlers.print());

	}

}

5.Mockito.spy ---Mock同一个类中的函数调用

一般都是根据单元测试规范设计程序,尽量避免同一个类中的函数引用。
但代码实际开发过程中,总有很多特殊情况。如果有同一个类之间的引用,在测试添加函数时就出现了麻烦事,该如何Mock 这个同一个类中的函数调用呢?

import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

	public void test(Integer a, Integer b) {
		System.out.println("this is test method begin");

		Integer sum = add(a, b);

		System.out.println("this is test method end = " + sum);

	}

	public Integer add(Integer a, Integer b) {
		System.out.println("this is add method");
		return a + b;
	}
}

测试类

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = StudentServiceImpl.class)
class StudentServiceImplTest {

	@Autowired
	private StudentServiceImpl studentService;
	
	@Test
	void testTest() {
		//spy这个类
		StudentServiceImpl spyStudentService = Mockito.spy(studentService);
		//test
		spyStudentService.test(3, 4);
		//测试同一个类中的方法
		Mockito.verify(spyStudentService, Mockito.times(1)).add(3, 4);
	}

}

有需要测试的controller 如下

import java.io.IOException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/student")
public class StudentController {

	@Autowired
	private StudentService studentService;

	@GetMapping("/findAll")
	public ResponseEntity findAll() throws IOException {
		List<Student> students = studentService.findAll();
		for (Student student : students) {
			System.out.println("student name=" + student.getName());
		}
		return ResponseEntity.ok(students);
	}

}

添加依赖 

		<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
		<dependency>
			<groupId>org.mockito</groupId>
			<artifactId>mockito-all</artifactId>
			<version>2.0.2-beta</version>
			<scope>test</scope>
		</dependency>

测试类如下

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
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.result.MockMvcResultMatchers;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@AutoConfigureMockMvc
@SpringBootTest(classes = StudentController.class)
public class StudentControllerTest {

	@Autowired
	private MockMvc mockMvc;

	// @MockBean 创建接口的mock实例
	// 这个studentService实例就拥有StudentService接口的所有方法
	// 并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。
	@MockBean
	private StudentService studentService;

	@Test
	public void testFindAll() throws Exception {

		// mock data
		List<Student> students = new ArrayList<Student>();
		Student student1 = new Student();
		student1.setName("xxx");
		students.add(student1);
		Student student2 = new Student();
		student2.setName("yyy");
		students.add(student2);

		//mockito stub 打桩
		Mockito.when(studentService.findAll()).thenReturn(students);

		//测试
		mockMvc.perform(MockMvcRequestBuilders.get("/student/findAll"))

				.andExpect(MockMvcResultMatchers.status().isOk())

				.andDo(MockMvcResultHandlers.print());
		
		
		// 验证方法执行过一次
		verify(studentService, times(1)).findAll();

	}

}

运行测试类,控制台输出

student name=xxx
student name=yyy

posted on 2020-07-20 10:47  dreamstar  阅读(566)  评论(0编辑  收藏  举报