例子:向事件表中插入事件数据,若该事件涉及到的实体在实体表中不存在,则将实体再插入实体表,同时向实体和事件关系表插入数据。事件和实体用随机生成的UUID作为唯一标识。
service层实现:
// EventService.java
@Service
public class EventService {
@Autowired
private EventMapper eventMapper;
@Autowired
private EntityService entityService;
public void insertEvent(String title, LocalDateTime eventTime, String nation, String entityName) {
String eventUuid = UUID.randomUUID().toString();
//创建事件
EventEntity eventEntity = new EventEntity();
eventEntity.setEventUuid(eventUuid);
eventEntity.setTitle(title);
eventEntity.setEventTime(eventTime);
eventEntity.setNation(nation);
//向事件表插入事件
eventMapper.insertEvent(eventEntity);
//判断实体表是否存在实体,不存在则实体表插入实体、实体和事件关系表插入关系
Boolean flag = entityService.insertEntityIfNotExists(entityName, eventUuid);
if(flag){
System.out.println("实体不存在,插入");
}else {
System.out.println("实体存在,不插入");
}
}
}
// EntityService.java
@Service
public class EntityService {
@Autowired
private EntityMapper entityMapper;
@Autowired
private AssociationMapper associationMapper;
public Boolean insertEntityIfNotExists(String entityName, String eventUuid) {
String entityUuid = UUID.randomUUID().toString();
//根据实体名查询数据
List<Entity> existingEntity = entityMapper.findByEntityName(entityName);
//没有该数据则执行插入逻辑
if (existingEntity.size() == 0) {
Entity entityEntity = new Entity();
entityEntity.setEntityUuid(entityUuid);
entityEntity.setEntityName(entityName);
//关系表的插入
associationMapper.insert(entityUuid, eventUuid);
//实体表的插入
entityMapper.insertEntity(entityEntity);
return true;
}
else{
return false;
}
}
}
有了这样一个例子之后,现在我想知道我 EventService 里面的insertEvent方法逻辑对不对,当然可以直接进行测试,但是这种情况下,我们会对数据库进行操作,既有访问数据库,又有插入数据库的操作,很多时候可能是我们不愿意看到的。此外还要启动整个项目,若当项目过大时,启动一次项目耗时也是不愿意看到的情况。
这个时候mock测试的作用就体现出来了。mock我简单粗暴的在这种情况下的理解就是“模拟”,将你不愿意真正执行的函数进行mock模拟,模拟的函数不会真正的去执行函数内部代码,而是通过你给定函数形参和指定返回值进行下一步的操作即可。
以下就是针对“EventService 里面的insertEvent方法逻辑对不对”写的mock单元测试代码:
package com.example.springbootexam.service;
import com.example.springbootexam.mapper.EventMapper;
import com.example.springbootexam.pojo.EventEntity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.LocalDateTime;
import static org.mockito.Mockito.*;
/**
* ClassName:EventServiceTest
* Package:com.example.springbootexam.service
* Description:
*
* @Date:2023/10/9 14:33
* @Author:QiTao
*/
@RunWith(MockitoJUnitRunner.class)
public class EventServiceTest {
@InjectMocks
private EventService eventService;
@Mock
private EventMapper eventMapper;
@Mock
private EntityService entityService;
@Test
public void testInsertEvent() {
// 创建测试数据
String title = "ttt卡尔·文森号航空母舰于美国西海岸航行";
LocalDateTime eventTime = LocalDateTime.of(2023, 10, 7, 0, 0);
String nation = "美国西海岸";
String entityName = "卡尔·文森号航空母舰";
String eventUuid = "77f66316-c276-49c8-96fa-73e041efc8f7";
// 模拟eventMapper的行为
doNothing().when(eventMapper).insertEvent(any(EventEntity.class));
// 模拟entityService的行为
when(entityService.insertEntityIfNotExists(eq(entityName), any(String.class))).thenReturn(true);
// 调用待测试的方法
eventService.insertEvent(title, eventTime, nation, entityName);
// 验证行为是否符合预期
verify(entityService, times(1)).insertEntityIfNotExists(eq(entityName), any(String.class));
verify(eventMapper, times(1)).insertEvent(any(EventEntity.class));
}
}
1、@RunWith(MockitoJUnitRunner.class)
就是做一些配置吧,固定写法就没深究。
2、@InjectMocks注解
都说是“打桩”,我简单理解就是你要测试哪个类就给哪个类加这个注解(定位?)
3、@Mock注解
就是对你需要模拟的函数所在的类上加的,比如这里,我想模拟eventMapper的insertEvent插入函数和entityService的insertEntityIfNotExists函数,就在这两个类上加@Mock。
这就是基本的固定套路了。后面便是test逻辑的编写。
4、doNothing()
就是告诉编辑器遇到这个函数别真去执行其中代码。
5、when(entityService.insertEntityIfNotExists(eq(entityName), any(String.class))).thenReturn(true);
我觉得就是“当”遇到insertEntityIfNotExists时就返回(thenReturn)true就行了。至于其中的eq(entityName)就是期望代码运行时的参数和我测试数据的值一致,不一致就抛异常,any(String.class)就是指定string类型的任意值就行。
6、verify(eventMapper, times(1)).insertEvent(any(EventEntity.class));
验证insertEvent是否是执行了一次(不是真的执行代码了)。