Mockito 是一个模拟测试框架,主要功能是在单元测试中模拟类/对象的行为。

1 为什么要使用Mockito?

Mock可以理解为创建一个虚假的对象,或者说模拟出一个对象.在测试环境中用来替换掉真实的对象,以达到我们可以

  • 验证该对象的某些方法的调用情况,调用了多少次,参数是多少.
  • 给这个对象的行为做一个定义,来指定返回结果或指定特定的动作.

2 Mockito数据隔离

根据 JUnit 单测隔离 ,当 Mockito 和 JUnit 配合使用时,也会将非static变量或者非单例隔离开。

比如使用 @Mock 修饰的 mock 对象在不同的单测中会被隔离开。

3 Mock方法

3.1 mock 对象:创建模拟对象

3.1.1 mock 实例对象/实例方法: mock(Class classToMock)

mock方法来自org.mockito.Mock,它标识可以mock一个对象或者是接口

public static <T> mock(Class<T> classToMock);
  • 入参:classToMock: 待mock对象的class类
  • 返回:mock出来的类的对象(此时对象内依旧无数据)
Random random = Mockito.mock(Random.class);

when(random.nextBoolean()).thenReturn(true);
logger.info("result: {}", random.nextBoolean());//result: true

3.1.2 mock 类对象/静态方法: mockStatic(T.class)

注:QueryFactory#query为静态方法

/**
* 静态类 T、静态方法的 Mock
* 1、mock方式: MockedStatic<T> mockClass = mockStatic(T.class); mockClass.when(...).thenReturn(...)
* 2、静态类/静态方法需在多个测试用例中 mock 时,需使用: `try-with-resources`的方式,以解决此问题"To create a new mock, the existing static mock registration must be deregistered"
*/
try(MockedStatic<QueryFactory> mockedQueryFactory = Mockito.mockStatic(QueryFactory.class)){
mockedQueryFactory.when( () -> QueryFactory.query(any(), any(), any(), any()) ).thenReturn(result); signalNamesMappingCacheService.init();
try {
commonSearchBizSevice.executeQuery(requestInfo);//内部将调用 QueryFactory.query(...)
} catch (Exception e) {
logger.info("happens a exception: SUCCESS");//内置脚本反射调用失败
}
}

3.2 Stub 存根 : 为mock对象规定预期的目标执行结果

简述

存根的意思就是给mock对象规定一行的行为,使其按照我们的要求来执行具体的动作。

样例程序

demo1

 //You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class); //stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(0)).thenReturn("two");
when(mockedList.get(1)).thenThrow(new RuntimeException()); //following prints "two"
System.out.println(mockedList.get(0)); //following throws runtime exception
System.out.println(mockedList.get(1)); //following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999)); //Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
//If your code doesn't care what get(0) returns, then it should not be stubbed.
verify(mockedList).get(0);

demo2

@Test
public void test(){
// 创建Mock对象,参数可以是类或者接口
List<String> list = mock(List.class); // 设置方法的预期返回值
when(list.get(0)).thenReturn("zuozewei");
when(list.get(1)).thenThrow(new RuntimeException("test exception")); String result = list.get(0); // 验证方法调用
verify(list).get(0); //断言,list的第一个元素是否是 "zuozwei"
Assert.assertEquals(result,"zuozewei");
}

常用方法

mock实例对象的常用方法

  • when(mockObject.actionMethod).thenReturn(String t) 设置方法的目标返回值
  • when(mockObject.actionMethod).thenThrow(Throwable... throwables) 让方法抛出异常
  • when(mockObject.actionMethod).thenAnswer(Answer answer) / then(Answer answer) 自定义方法处理逻辑
  • when(mockObject.actionMethod).thenCallRealMethod() 调用 spy 对象的真实方法

mock类对象的常用方法

  • mockStaticObject.when(Verification verification).thenReturn(...)
MockedStatic<QueryFactory> mockedQueryFactory = Mockito.mockStatic(QueryFactory.class)
mockedQueryFactory.when( () -> QueryFactory.query(any(), any(), any(), any()) ).thenReturn(result);

3.3 参数匹配:精确匹配与模糊匹配(anyXXX)

import org.junit.Assert;
import org.junit.Test; import java.util.List; import static org.mockito.Mockito.*; public class MockitoDemo { @Test
public void test() {
List mockList = mock(List.class); Assert.assertEquals(0, mockList.size());
Assert.assertEquals(null, mockList.get(0)); mockList.add("a"); // 调用 mock 对象的写方法,是没有效果的 Assert.assertEquals(0, mockList.size()); // 没有指定 size() 方法返回值,这里结果是默认值
Assert.assertEquals(null, mockList.get(0)); // 没有指定 get(0) 返回值,这里结果是默认值 when(mockList.get(0)).thenReturn("a"); // 指定 get(0)时返回 a Assert.assertEquals(0, mockList.size()); // 没有指定 size() 方法返回值,这里结果是默认值
Assert.assertEquals("a", mockList.get(0)); // 因为上面指定了 get(0) 返回 a,所以这里会返回 a Assert.assertEquals(null, mockList.get(1)); // 没有指定 get(1) 返回值,这里结果是默认值 // 参数匹配:精确匹配
when(mockStringList.get(0)).thenReturn("a");
when(mockStringList.get(1)).thenReturn("b"); Assert.assertEquals("a", mockStringList.get(0));
Assert.assertEquals("b", mockStringList.get(1)); // 参数匹配:模糊匹配
when(mockStringList.get(anyInt())).thenReturn("a"); // 使用 Mockito.anyInt() 匹配所有的 int Assert.assertEquals("a", mockStringList.get(0));
Assert.assertEquals("a", mockStringList.get(1));
} //eq(...)
@Test
public void test2(){
...
when(databaseConnectionManager.borrowConnector(eq(DsType2.INFLUXDB), eq(dataSource), any(Integer.class))).thenReturn(connector);
...
}
}

3.4 reset(mockObject) : 重置之前自定义的返回值和异常

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*; public class MockitoDemo {
static class ExampleService {
public int add(int a, int b) {
return a+b;
}
} @Test
public void test() {
ExampleService exampleService = mock(ExampleService.class);
// mock 对象方法的默认返回值是返回类型的默认值
Assert.assertEquals(0, exampleService.add(1, 2));
// 设置让 add(1,2) 返回 100
when(exampleService.add(1, 2)).thenReturn(100);
Assert.assertEquals(100, exampleService.add(1, 2));
// 重置 mock 对象,add(1,2) 返回 0
reset(exampleService);
Assert.assertEquals(0, exampleService.add(1, 2));
} @Test
public void test2() {
ExampleService exampleService = spy(new ExampleService());
// spy 对象方法调用会用真实方法,所以这里返回 3
Assert.assertEquals(3, exampleService.add(1, 2));
// 设置让 add(1,2) 返回 100
when(exampleService.add(1, 2)).thenReturn(100);
Assert.assertEquals(100, exampleService.add(1, 2));
// 重置 spy 对象,add(1,2) 返回 3
reset(exampleService);
Assert.assertEquals(3, exampleService.add(1, 2));
}
}

4 Mock 注解

@Mock 注解

@mock快速创建mock的方法,使用

@mock注解需要搭配MockitoAnnotations.openMocks(testClass)方法一起使用.

   public class ArticleManagerTest {

       @Mock
private ArticleCalculator calculator;
@Mock
private ArticleDatabase database;
@Mock
private UserProvider userProvider; private ArticleManager manager; @org.junit.jupiter.api.Test
void testSomethingInJunit5() {
// 初始化mock对象
MockitoAnnotations.openMocks(testClass);
//Mockito.mock(class);
Mockito.spy(class);
} // 简化
// @BeforeEach
// void setUp() {
// MockitoAnnotations.openMocks(this);
// } }

@Spy注解 与 Spy方法

spy 和 mock不同,不同点是:

  • spy 的参数是对象实例,mock 的参数是 class。
  • 被 spy 的对象,调用其方法时默认会走真实方法。mock 对象不会。

@Spy注解需要搭配MockitoAnnotations.openMocks(testClass)方法一起使用.

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*; class ExampleService {
int add(int a, int b) {
return a+b;
}
} public class MockitoDemo { // 测试 spy
@Test
public void test_spy() {
ExampleService spyExampleService = spy(new ExampleService());
// 默认会走真实方法
Assert.assertEquals(3, spyExampleService.add(1, 2));
// 打桩后,不会走了
when(spyExampleService.add(1, 2)).thenReturn(10);
Assert.assertEquals(10, spyExampleService.add(1, 2));
// 但是参数比匹配的调用,依然走真实方法
Assert.assertEquals(3, spyExampleService.add(2, 1));
} // 测试 mock
@Test
public void test_mock() {
ExampleService mockExampleService = mock(ExampleService.class);
// 默认返回结果是返回类型int的默认值
Assert.assertEquals(0, mockExampleService.add(1, 2));
}
}

spy 对应注解 @Spy,和 @Mock 是一样用的。

import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy; import static org.mockito.Mockito.*; class ExampleService {
int add(int a, int b) {
return a+b;
}
} public class MockitoDemo {
@Spy
private ExampleService spyExampleService; @Test
public void test_spy() {
MockitoAnnotations.openMocks(this);
Assert.assertEquals(3, spyExampleService.add(1, 2));
when(spyExampleService.add(1, 2)).thenReturn(10);
Assert.assertEquals(10, spyExampleService.add(1, 2));
}
}

@InjectMocks : 注解注入 mock 对象

mockito 会将 @Mock、@Spy 修饰的对象自动注入到 @InjectMocks 修饰的对象中。

注入方式有多种,mockito 会按照下面的顺序尝试注入:

  • 构造函数注入
  • 设值函数注入(set函数)
  • 属性注入
package demo;
import java.util.Random; public class HttpService {
public int queryStatus() {
// 发起网络请求,提取返回结果
// 这里用随机数模拟结果
return new Random().nextInt(2);
}
}
package demo;

public class ExampleService {
private HttpService httpService;
public String hello() {
int status = httpService.queryStatus();
if (status == 0) {
return "你好";
}
else if (status == 1) {
return "Hello";
}
else {
return "未知状态";
}
}
}
import org.junit.Assert;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import static org.mockito.Mockito.when; public class ExampleServiceTest { @InjectMocks // 将httpService主动注入
private ExampleService exampleService = new ExampleService(); @Mock
private HttpService httpService; @Test
public void test01() {
MockitoAnnotations.initMocks(this);
when(httpService.queryStatus()).thenReturn(0);
Assert.assertEquals("你好", exampleService.hello());
}
}

验证和断言: verify(...)方法

验证是校验待验证的对象是否发生过某些行为,Mockito中验证的方法是:verify

verify(mock).someMoethod("some arg");
verify(mock,times(100)).someMoethod("some arg");

使用verify验证(Junit的断言机制):

  @Test
void check() {
Random random = Mockito.mock(Random.class);
System.out.println(random.nextInt());
Mockito.verify(random,Mockito.times(2)).nextInt();
}

Verify配合times()方法,可以校验某些操作发生的次数

 //using mock
mockedList.add("once"); mockedList.add("twice");
mockedList.add("twice"); mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times"); // 默认调用1次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once"); // 自定义调用多次
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times"); // 从不调用
verify(mockedList, never()).add("never happened"); // atLeast()/atMost() 至少调用 / 之多调用
verify(mockedList, atMostOnce()).add("once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times"); // 超时验证
verify(mock, timeout(100)).someMethod();
verify(mock, timeout(100).times(1)).someMethod(); //只要 someMethod() 在 100 毫秒内被调用 2 次,就会通过
verify(mock, timeout(100).times(2)).someMethod(); //someMethod() 至少在 100 毫秒内被调用 2 次,就会通过
verify(mock, timeout(100).atLeast(2)).someMethod();

5 Mock测试场景

MockMVC 测试 (未亲测)

对于前后端分离的项目而言,无法直接从前端静态代码中测试接口的正确性,因此可以通过MockMVC来模拟HTTP请求。基于RESTful风格的SpringMVC的测试,可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。

初始化MockMvc对象

@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc; //在每个测试方法执行之前都初始化MockMvc对象
@BeforeEach
public void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

接口测试

1 controller 层

/**
* id:\\d+只匹配数字
* @param id
* @return
*/
@GetMapping("/user/{id:\\d+}")
public User getUserById(@PathVariable Long id) { return userService.getById(id);
}

2 Mockito构建自定义返回结果

userService.getById并没有返回结果,但是我们的测试并不关心userService.getById这个方法是否正常,只是在我们的测试中需要用到这个方法,所以我们可以Mock掉UserService的getById方法,自己定义返回的结果。

@MockBean
private UserService userService; @Test
void getUserById() throws Exception { User user = new User();
user.setId(1);
user.setNickname("yunqing");
//Mock一个结果,当userService调用getById的时候,返回user
doReturn(user).when(userService).getById(any()); //perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理
mockMvc.perform(MockMvcRequestBuilders
//构造一个get请求
.get("/user/1")
//请求类型 json
.contentType(MediaType.APPLICATION_JSON))
// 期望的结果状态 200
.andExpect(MockMvcResultMatchers.status().isOk())
//添加ResultHandler结果处理器,比如调试时 打印结果(print方法)到控制台
.andDo(MockMvcResultHandlers.print());
}

3 传参数

@Test
void getUserByUsername() throws Exception {
// perform : 执行请求 ;
mockMvc.perform(MockMvcRequestBuilders
//MockMvcRequestBuilders.get("/url") : 构造一个get请求
.get("/user/getUserByName")
//传参
.param("username","admin")
// 请求type : json
.contentType(MediaType.APPLICATION_JSON))
// 期望的结果状态 200
.andExpect(MockMvcResultMatchers.status().isOk());
}

4 期望返回结果集有两个元素

@Test
void getAll() throws Exception {
User user = new User();
user.setNickname("yunqing");
List<User> list = new LinkedList<>();
list.add(user);
list.add(user);
//Mock一个结果,当userService调用list的时候,返回user
when(userService.list()).thenReturn(list);
//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理
mockMvc.perform(MockMvcRequestBuilders
//构造一个get请求
.get("/user/list")
//请求类型 json
.contentType(MediaType.APPLICATION_JSON))
// 期望的结果状态 200
.andExpect(MockMvcResultMatchers.status().isOk())
//期望返回的结果集合有两个元素
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2))
//添加ResultHandler结果处理器,比如调试时 打印结果(print方法)到控制台
.andDo(MockMvcResultHandlers.print());
}

5 测试Post请求

@Test
void insert() throws Exception { User user = new User();
user.setNickname("yunqing");
String jsonResult = JSONObject.toJSONString(user);
//直接自定义save返回true
when(userService.save(any())).thenReturn(true);
// perform : 执行请求 ;
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
//MockMvcRequestBuilders.post("/url") : 构造一个post请求
.post("/user/insert")
.accept(MediaType.APPLICATION_JSON)
//传参,因为后端是@RequestBody所以这里直接传json字符串
.content(jsonResult)
// 请求type : json
.contentType(MediaType.APPLICATION_JSON))
// 期望的结果状态 200
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();//返回结果 int statusCode = mvcResult.getResponse().getStatus();
String result = mvcResult.getResponse().getContentAsString();
//单个断言
Assertions.assertEquals(200, statusCode);
//多个断言,即使出错也会检查所有断言
assertAll("断言",
() -> assertEquals(200, statusCode),
() -> assertTrue("true".equals(result))
);
  • 常用API
//使用jsonPaht验证返回的json中code、message字段的返回值
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("00000"))
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("成功")) //body属性不为空
.andExpect(MockMvcResultMatchers.jsonPath("$.body").isNotEmpty()) // 期望的返回结果集合有2个元素 , $: 返回结果
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2));

Y 样例集合

Y.1 基于 Mockito 框架的测试类

Y.1.1 Code

import cn.johnny.bd.dataservice.biz.cansignal.service.SignalNamesMappingCacheService;
import cn.johnny.bd.dataservice.biz.dataservice.mapper.IQueryJobMapper;
import cn.johnny.bd.dataservice.common.debug.Print;
import cn.johnny.bd.dataservice.common.sqlTemplate.GlobalInlineVariableNamesEnum;
import cn.johnny.bd.dataservice.model.entity.SignalFieldMapping;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
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.util.AopTestUtils;
import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.*; //@ExtendWith(MockitoExtension.class) // MockitoJUnitRunner 为开发者提供了 Mockito 框架使用情况的自动验证,以及自动执行 initMocks()
@RunWith(MockitoJUnitRunner.class) // SpringJUnit4ClassRunner / SpringRunner / MockitoJUnitRunner public class MockitoTest {
private static final Logger logger = LoggerFactory.getLogger(MockitoTest.class); // @Mock
// private String canParseMatrixConfigDBName; // [X] 无法 Mock final class /**
* 使用 @Mock 注解封装需要被模拟调用的对象 | @Mock: 创建一个Mock.
*/
@Mock
private IQueryJobMapper queryJobMapper; @Mock
private LoadingCache<String, Map<String, SignalFieldMapping>> signalNamesMappingCache; /**
* 使用 @InjetcMock 注解是为了向里面添加 @Mock 注入的对象 | @InjectMocks: 创建一个实例,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
*/
@InjectMocks
private SignalNamesMappingCacheService cacheService; @Before
public void setup(){
ReflectionTestUtils.setField(cacheService, "canParseMatrixConfigDBName", "johnny_bdp");//final Class(String) 无法成为 Mock 对象。故通过这种 Spring 提供的 ReflectionTestUtils 工具注入该属性
// ReflectionTestUtils.setField(cacheService, "queryJobMapper", queryJobMapper);
// ReflectionTestUtils.setField(cacheService, "signalNamesMappingCache", signalNamesMappingCache);
} /**
* @description
* // step1 ready dataset for test stubs(准备测试桩所需的基础数据集)
* // step2 setup test stubs(设置测试桩)
* // step3 Run the target test class and method (执行测试目标类的目标方法)
* // step4 assert , verify and end over(断言、验证与结束)
*/
@Test
public void getSignalNamesMappingTestSuccess(){
// step1 ready dataset for test stubs(准备测试桩所需的基础数据集)
List<SignalFieldMapping> signalList = new ArrayList<>();
SignalFieldMapping signalFieldMapping1 = new SignalFieldMapping(1, "bcm_pepsPowerMode", "bcm_peps_power_mode", "bcm_pepsPowerMode", "{\"0\":\"DEFAULT_POWERMODE\",\"1\":\"OFF_POWERMODE\",\"2\":\"ON_POWERMODE\",\"3\":\"RESERVE_POWERMODE\",\"4\":\"RESERVE_POWERMODE1\",\"5\":\"RESERVE_POWERMODE2\",\"6\":\"RESERVE_POWERMODE3\",\"7\":\" INVALID_POWERMODE\"}");
SignalFieldMapping signalFieldMapping2 = new SignalFieldMapping(2, "bcm_pepsKeyNotInCarInd", "bcm_peps_key_not_in_car_ind", "Keyisnotincarindication.钥匙离开车内提示.", "{\"0\":\"NO_WARNING\",\"1\":\" WARNING\"}");
signalList.add(signalFieldMapping1);
signalList.add(signalFieldMapping2); CacheStats cacheStats = new CacheStats(1,1,1,1,1,1); String globalInlineVariableNamesCode = GlobalInlineVariableNamesEnum.SIGNAL_NAMES_ORIGIN_MAPPING.getCode(); // step2 setup test stubs(设置测试桩)
when(queryJobMapper.getSignalFieldMapping("johnny_bdp")).thenReturn(signalList);
when(signalNamesMappingCache.stats()).thenReturn(cacheStats); // step3 Run the target test class and method (执行测试目标类的目标方法)
cacheService.init();
Map<String, SignalFieldMapping> signalFieldMappingMap = cacheService.getSignalNamesMapping(globalInlineVariableNamesCode); // step4 assert , verify and end over(断言、验证与结束)
if(!signalFieldMappingMap.isEmpty()){
logger.debug("signalFieldMappingMap is not empty, content as follows:");
Print.print(signalFieldMappingMap);
} Assert.assertNotNull(signalFieldMappingMap);
} /**
* 基本模拟测试1
* @reference-doc
* [1] 走进Java接口测试之Mock(概念篇) - Tencent Cloud - https://cloud.tencent.com/developer/article/1465589
*/
@Test
public void mockitoBaseTest(){
// 创建Mock对象,参数可以是类或者接口
List<String> list = mock(List.class); // 设置方法的预期返回值
when(list.get(0)).thenReturn("zuozewei");
when(list.get(1)).thenThrow(new RuntimeException("test exception")); String result = list.get(0);
logger.warn("result:{}", result);// zuozewei // 验证方法调用, 若有异常时会直接抛 Exception
String verifyResult = verify(list).get(0);
logger.warn("verifyResult:{}", verifyResult);//null //断言,list的第一个元素是否是 "zuozwei"
Assert.assertEquals(result,"zuozewei");
} /**
* 基本模拟测试2
*/
@Test
public void mockBaseTest2(){
//一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可以选择性的验证感兴趣的交互
//You can mock concrete classes, not only interfaces
// 你可以mock具体的类型,不仅只是接口
//创建 mock 对象,mock 1个 List 接口
// List mockedList = mock(List.class);
//若不使用静态导入,则:必须使用 Mockito 调用
// List mockedList = Mockito.mock(List.class);
LinkedList mockedList = mock(LinkedList.class); //stubbing
// 测试桩
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException()); //following prints "first"
// 输出 “first”
System.out.println(mockedList.get(0)); //following prints "null" because get(999) was not stubbed
// 因为get(999) 没有打桩,因此输出null
System.out.println(mockedList.get(999)); //Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns then something else breaks (often before even verify() gets executed).
//If your code doesn't care what get(0) returns then it should not be stubbed. Not convinced? See here.
// 验证 get(0) 被调用的次数
verify(mockedList).get(0); // following throws runtime exception
// 抛出异常 | 因 get(1) 的测试桩是抛出异常
// System.out.println(mockedList.get(1));
} /**
* 基本模拟测试3
*/
@Test
public void mockBaseTest3(){
// 生成 mock 对象
Foo mockFoo = mock(Foo.class); // 测试桩
when(mockFoo.bool(anyString(), anyInt(), any(Object.class))).thenReturn(true); // 验证/断言
Assert.assertTrue(mockFoo.bool("A", 1, "A"));
Assert.assertTrue(mockFoo.bool("B", 10, new Object())); // 测试桩
when(mockFoo.bool(eq("false"), anyInt(), any(Object.class))).thenReturn(false);
// 验证/断言
Assert.assertFalse(mockFoo.bool("false", 10, new Object()));
}
} class Foo {
boolean bool(String str, int i, Object obj) {
return false;
} int in(boolean b, List<String> strs) {
return 0;
} int bar(byte[] bytes, String[] s, int i) {
return 0;
}
}

Y.1.2 dependency

<!-- org.junit.runner.RunWith;org.junit.Assert;org.junit.Before;org.junit.Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!-- 4.13.1 -->
<version>${junit.version}</version>
<scope>test</scope>
</dependency> <!-- org.slf4j.Logger ; org.slf4j.LoggerFactory; -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<!-- 1.7.25 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<!-- 1.7.25 -->
</dependency> <!--
org.mockito.Mockito; org.mockito.MockedStatic;
org.mockito.junit.MockitoJUnitRunner;
org.mockito.InjectMocks; org.mockito.Mock;
org.mockito.ArgumentMatchers.any; org.mockito.ArgumentMatchers.eq
-->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<!-- 3.4.0 -->
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<!-- 3.4.0 -->
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<!-- 3.4.0 -->
<version>${mockito.version}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>mockito-core</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>com.github.jsonzou</groupId>
<artifactId>jmockdata</artifactId>
<!-- 4.3.0 -->
<version>${jmockdata.version}</version>
<scope>test</scope>
</dependency>

Y.2 基于 spring 框架的测试类

Y.2.1 Code

import cn.johnny.bd.dataservice.TestApplication;
import cn.johnny.bd.dataservice.biz.cansignal.service.SignalNamesMappingCacheService;
import cn.johnny.bd.dataservice.biz.dataservice.mapper.IQueryJobMapper;
import cn.johnny.bd.dataservice.common.debug.Print;
import cn.johnny.bd.dataservice.common.sqlTemplate.GlobalInlineVariableNamesEnum;
import cn.johnny.bd.dataservice.model.entity.SignalFieldMapping;
//import com.github.jsonzou.jmockdata.MockConfig;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
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.SpringJUnit4ClassRunner; import javax.annotation.Resource;
import java.util.Map; @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class SpringJUnitTest {
private static final Logger logger = LoggerFactory
.getLogger(SpringJUnitTest.class); @Resource
private IQueryJobMapper queryJobMapper; @Resource
private SignalNamesMappingCacheService cacheService; @Before
public void init() { } @Test
public void test(){
String globalInlineVariableNamesCode = GlobalInlineVariableNamesEnum.SIGNAL_NAMES_ORIGIN_MAPPING.getCode();
Map<String, SignalFieldMapping> signalFieldMappingMap = cacheService.getSignalNamesMapping(globalInlineVariableNamesCode);
Print.print(signalFieldMappingMap);
Assert.assertNotNull(signalFieldMappingMap);
}
} ////////////// TestApplication ////////////// import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ComponentScan; @SpringBootApplication
@ComponentScan({"cn.johnny"})
public class TestApplication { public static void main(String[] args) {
new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);
} }

Y.2.2 dependency

<!-- org.junit.runner.RunWith;org.junit.Assert;org.junit.Before;org.junit.Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!-- 4.13.1 -->
<version>${junit.version}</version>
<scope>test</scope>
</dependency> <!-- org.slf4j.Logger ; org.slf4j.LoggerFactory; -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<!-- 1.7.25 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<!-- 1.7.25 -->
</dependency> <!-- org.springframework.boot.test.context.SpringBootTest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.3.12.RELEASE</version>
<!-- <scope>compile</scope> -->
</dependency> <!-- org.springframework.test.context.junit4.SpringJUnit4ClassRunner; org.springframework.test.util.ReflectionTestUtils; -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<!-- 5.2.15.RELEASE -->
<scope>test</scope>
</dependency>

K 模拟测试框架小结

mock-server: API/接口测试框架

Mokito

  • Mockito是mocking框架,它让你用简洁的API做测试。而且Mockito简单易学,它可读性强和验证语法简洁。
  • Mockito是GitHub上使用最广泛的Mock框架,并与JUnit结合使用。
  • Mockito框架可以创建和配置mock对象。
  • 使用Mockito简化了具有外部依赖的类的测试开发。

JMockData

小结

常用的mock框架有:EasyMockJMockMockitoPowerMockito。比较常用的是Mockito

比较项 EasyMock JMock Mockito PowerMockito
final方法 不支持 不支持 不支持 支持
private方法 不支持 不支持 不支持 支持
静态方法 不支持 不支持 支持 支持
SpringBoot依赖 实现较为复杂 实现较为复杂 默认依赖 基于Mockito扩展
API风格 略复杂 略复杂 简单 简单

X 参考文献

[Java SE/Junit] 基于Java的单元测试框架Mockito的更多相关文章

  1. Java SE 核心 II【Collection 集合框架】

    Collection集合框架 在实际开发中,需要将使用的对象存储于特定数据结构的容器中.而 JDK 提供了这样的容器——集合框架,集合框架中包含了一系列不同数据结构(线性表.查找表)的实现类.集合的引 ...

  2. Java编程思想-基于注解的单元测试

    Junit的测试方法命名不一定以test开头 上面介绍的atunit已经很老了,现在junit测试框架已经基本注解了

  3. Java SE之网络编程:知识框架

  4. 【Java SE】利用Java的for循环加random制作小学试卷

    前期介绍:很多同学以为学习一门编程语言,一定要学到很高深的时候才可以做项目,其实不然,很多时候我们不需要学到面向对象的思想,就可以从事一些小项目的开发,来增加自己对开发的热情,比如现在我就可以利用Ja ...

  5. Java SE教程

    第0讲 开山篇 读前介绍:本文中如下文本格式是超链接,可以点击跳转 >>超链接<< 我的学习目标:基础要坚如磐石   代码要十份规范   笔记要认真详实 一.java内容介绍 ...

  6. Java SE、Java EE和Java ME有什么区别?

    Java现在已不仅仅是一种语言,从广义上说,它代表了一个技术体系.该体系根据应用方向的不同主要分为Java SE.Java EE和Java ME的3个部分. 1998年12月份Sun公司公布的Java ...

  7. Oracle Java SE远程安全漏洞(CVE-2013-5878)

    漏洞版本: Oracle Java SE 7u45 Oracle Java SE 6u65 漏洞描述: BUGTRAQ ID: 64927 CVE(CAN) ID: CVE-2013-5878 Jav ...

  8. java的几个版本以及jre,jdk等概念——【转载】JDK、Java SE、Java EE、Java ME我该选

    我们平时使用的一些软件,有一部分需要Java环境的支持,但是SUN那么多的产品,让人眼花缭乱的版本号,前看后看都差不多的缩写,让我们选择起来的时候常常望而却步,只好跟着感觉走.所以下面我要介绍的就是那 ...

  9. 在 Java SE 6 中监视和诊断性能问题

    Java™ Platform, Standard Edition 6 (Java SE) 专注于提升性能,提供的增强工具可以管理和监视应用程序以及诊断常见的问题.本文将介绍 Java SE 平台中监视 ...

  10. Java SE/EE/ME概念理解(Java版本发展历史)

    继上一篇文章http://www.cnblogs.com/EasonJim/p/6181981.html中说的区别,其实分析的不够彻底,因此再次在这里做详细的分析. 零.Java与Sun.Oracle ...

随机推荐

  1. base64与中文字符串互转

    实现代码如下 // 字符串转base64 getEncode64(str){ return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g ...

  2. 乘积小于K的子数组

    乘积小于K的子数组 给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目. 示例 1: 输入:nums = [10,5,2,6], k = 10 ...

  3. Idea项目构建时解决方法

    java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: GC overhead limit exceeded 整 ...

  4. 第一章 对程序员来说CPU是什么

    章节标题下方有几个问题,看完后便对第一章的内容有了大概的了解. 第一章观后感想: 第一章解释了CPU是什么,CPU相当于计算机的大脑,它的内部由数百万至数亿个晶体管构成. CPU所负责的就是解释和运行 ...

  5. The Little Book of Rust Books

    https://lborb.github.io/book/title-page.html https://blog.logrocket.com/unsafe-rust-how-and-when-not ...

  6. Hihocoder 1067

    最近公共祖先二 离线算法 /**/ #include <cstdio> #include <cstring> #include <cmath> #include & ...

  7. 西电oj 244题单词排序

    题目描述: 定义一个二维字符数组str[10][20],行号表示单词序号,列号表示单词最大长度, 输入一个正整数N(N≤10),表示单词数,使用函数wd_sort()完成单词的排序,按字母顺序从小到大 ...

  8. AWS、谷歌云、Azure:云计算安全功能比较

    每个云平台提供给客户用以保护其云资产安全工具和安全功能都不一样. 公有云安全建立在共担责任概念基础之上:大型云服务提供商交付安全超大规模环境,但保护推上云端一切是客户自己责任.对企业而言,这种安全责任 ...

  9. enobj.cn站有更新

    1:整体样式 2:可以折叠app列表 3:手机端样式 4: Blog链接到博客园

  10. Vscode 界面语言换成中文

    作为编译器,Visual Studio Code是非常多能化的,但是界面英文对于我这种菜鸟新手造成了很大的困扰. 所以切换成中文更方便. Ctrl+shift+x   在左侧出现"扩展&qu ...