字典数据service/controller单元测试完善

This commit is contained in:
gaoqr
2026-01-14 16:08:30 +08:00
parent f1e4dc9d18
commit 1e052edbe7
5 changed files with 648 additions and 180 deletions
@@ -17,4 +17,9 @@ public class TransConfiguration {
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
@Bean
public TransApi transApi(TransProperties transProperties) {
return new TransApi(transProperties.getAppId(), transProperties.getSecurityKey(), transProperties.getApiHost());
}
}
@@ -26,7 +26,6 @@ import com.cf.imes.module.system.dal.mysql.dict.DictDataMapper;
import com.cf.imes.module.system.enums.ErrorCodeConstants;
import com.cf.imes.module.system.enums.common.LanguageEnum;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.framework.trans.config.TransProperties;
import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
@@ -77,7 +76,7 @@ public class DictDataServiceImpl implements DictDataService {
private DictDataI18nMapper dictDatai18nMapper;
@Resource
private TransProperties transProperties;
private TransApi transApi;
@Resource
private DictDataI18nVersionMapper dictDataI18nVersionMapper;
@@ -245,7 +244,6 @@ public class DictDataServiceImpl implements DictDataService {
.collect(Collectors.joining("\n"));
// 请求翻译api
TransApi transApi = new TransApi(transProperties.getAppId(), transProperties.getSecurityKey(), transProperties.getApiHost());
String transResult = transApi.getTransResult(namesForQuery, "zh", to);
JSONObject obj = JSON.parseObject(transResult);
@@ -0,0 +1,207 @@
package com.cf.imes.module.system.controller.admin.dict;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataTransReqVO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDataDO;
import com.cf.imes.module.system.service.dict.DictDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 字典数据管理单元测试
*
* @author Gqr
* @since 2026/1/14 15:38
*/
@WebMvcTest(controllers = DictDataController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
class DictDataControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private DictDataService dictDataService;
@Autowired
private ObjectMapper objectMapper;
@Test
void createDictData_success() throws Exception {
DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
reqVO.setSort(1);
reqVO.setLabel("启用");
reqVO.setDictType("status");
reqVO.setValue("1");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
when(dictDataService.createDictData(any()))
.thenReturn(100L);
mockMvc.perform(post("/system/dict-data/create")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(100L));
}
@Test
void updateDictData_success() throws Exception {
DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
reqVO.setId(1L);
reqVO.setDictType("status");
reqVO.setValue("1");
reqVO.setLabel("启用");
reqVO.setSort(1);
reqVO.setStatus(1);
mockMvc.perform(put("/system/dict-data/update")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
// 核心断言:Service 被调用
verify(dictDataService).updateDictData(any(DictDataSaveReqVO.class));
}
@Test
void deleteDictData_success() throws Exception {
mockMvc.perform(delete("/system/dict-data/delete")
.param("id", "1"))
.andExpect(status().isOk());
verify(dictDataService).deleteDictData(1L);
}
@Test
void getSimpleDictDataList_success() throws Exception {
Map<String, List<DictDataSimpleRespVO>> mockResult = new HashMap<>();
mockResult.put("zh", List.of(new DictDataSimpleRespVO().setLabel("启用")));
when(dictDataService.getAllLanguageDictData()).thenReturn(mockResult);
mockMvc.perform(get("/system/dict-data/list-all-simple"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.zh[0].label").value("启用"));
verify(dictDataService).getAllLanguageDictData();
}
@Test
void getDictTypePage_success() throws Exception {
DictDataDO dictData = new DictDataDO();
dictData.setId(1L);
dictData.setLabel("启用");
PageResult<DictDataDO> pageResult = new PageResult<>();
pageResult.setTotal(1L);
pageResult.setList(List.of(dictData));
when(dictDataService.getDictDataPage(any()))
.thenReturn(pageResult);
mockMvc.perform(get("/system/dict-data/page")
.param("pageNo", "1")
.param("pageSize", "10"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.total").value(1))
.andExpect(jsonPath("$.data.list[0].label").value("启用"));
verify(dictDataService).getDictDataPage(any());
}
@Test
void getDictData_success() throws Exception {
DictDataDO dictData = new DictDataDO();
dictData.setId(1L);
dictData.setLabel("启用");
when(dictDataService.getDictData(1L)).thenReturn(dictData);
mockMvc.perform(get("/system/dict-data/get")
.param("id", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.label").value("启用"));
verify(dictDataService).getDictData(1L);
}
@Test
void export_success() throws Exception {
when(dictDataService.getDictDataPage(any()))
.thenReturn(new PageResult<>(List.of(), 0L));
mockMvc.perform(get("/system/dict-data/export")
.param("pageNo", "1")
.param("pageSize", "10"))
.andExpect(status().isOk());
verify(dictDataService).getDictDataPage(any());
}
@Test
void trans_success() throws Exception {
DictDataTransReqVO reqVO = new DictDataTransReqVO();
reqVO.setTargetLanguage("en");
mockMvc.perform(post("/system/dict-data/trans")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(dictDataService).trans(any(DictDataTransReqVO.class));
}
@Test
void getVersion_success() throws Exception {
when(dictDataService.getI18nVersion()).thenReturn(3);
mockMvc.perform(get("/system/dict-data/i18n/version"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(3));
verify(dictDataService).getI18nVersion();
}
@Test
void updateVersion_success() throws Exception {
mockMvc.perform(post("/system/dict-data/i18n/version"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(dictDataService).updateI18nVersion();
}
}
@@ -660,7 +660,7 @@ public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
() -> deptService.validUserLoginDept(dept.getId()));
assertEquals(ErrorCodeConstants.DEPT_NOT_ALLOWED_LOGIN.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("测试部-禁用"));
assertTrue(ex.getArgs()[0].toString().contains("测试部-禁用"));
}
@Test
@@ -693,7 +693,7 @@ public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
() -> deptService.validUserLoginDept(child.getId()));
assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("上级部门-禁用"));
assertTrue(ex.getArgs()[0].toString().contains("上级部门-禁用"));
}
@Test
@@ -740,7 +740,7 @@ public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
() -> deptService.validUserLoginDept(child.getId()));
assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("顶级部门-禁用"));
assertTrue(ex.getArgs()[0].toString().contains("顶级部门-禁用"));
}
@Test
@@ -3,60 +3,90 @@ package com.cf.imes.module.system.service.dict;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.ArrayUtils;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataTransReqVO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDataDO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDatai18nVersionDO;
import com.cf.imes.module.system.dal.dataobject.dict.DictTypeDO;
import com.cf.imes.module.system.dal.mysql.dict.DictDataI18nMapper;
import com.cf.imes.module.system.dal.mysql.dict.DictDataI18nVersionMapper;
import com.cf.imes.module.system.dal.mysql.dict.DictDataMapper;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Import(DictDataServiceImpl.class)
public class DictDataServiceImplTest extends BaseDbUnitTest {
class DictDataServiceImplTest extends BaseMockitoUnitTest {
@Resource
@InjectMocks
private DictDataServiceImpl dictDataService;
@Resource
@Mock
private DictDataMapper dictDataMapper;
@MockBean
@Mock
private DictDataI18nMapper dictDatai18nMapper;
@Mock
private DictDataI18nVersionMapper dictDataI18nVersionMapper;
@Mock
private DictTypeService dictTypeService;
@Mock
private StringRedisTemplate stringRedisTemplate;
@Mock
private TransApi transApi;
@Test
public void testGetDictDataList() {
void testGetDictDataList() {
// mock 数据
DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2)
.setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO01);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1)
.setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO02);
DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3)
.setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO03);
DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3)
.setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO04);
// 准备参数
Integer status = CommonStatusEnum.ENABLE.getStatus();
String dictType = "yunai";
when(dictDataMapper.selectListByStatusAndDictType(status, dictType)).thenReturn(Arrays.asList(dictDataDO01, dictDataDO02));
// 调用
List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType);
// 断言
@@ -66,268 +96,415 @@ public class DictDataServiceImplTest extends BaseDbUnitTest {
}
@Test
public void testGetDictDataPage() {
// mock 数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到
void testGetDictDataPage() {
// 准备模拟数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> {
o.setLabel("晨丰");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
List<DictDataDO> mockList = List.of(dbDictData);
DictDataPageReqVO reqVO = new DictDataPageReqVO();
reqVO.setLabel("");
reqVO.setLabel("");
reqVO.setDictType("yunai");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
// mock Mapper 返回值
PageResult<DictDataDO> dictDataDOPageResult = new PageResult<>();
dictDataDOPageResult.setList(mockList);
dictDataDOPageResult.setTotal(1L);
when(dictDataMapper.selectPage(reqVO)).thenReturn(dictDataDOPageResult);
// 调用 Service
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictData, pageResult.getList().get(0));
// 验证 Mapper 方法被调用
verify(dictDataMapper, times(1)).selectPage(reqVO);
}
@Test
public void testGetDictData() {
void testGetDictData() {
Long id = randomLongId();
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);
// 准备参数
Long id = dbDictData.getId();
when(dictDataMapper.selectById(id)).thenReturn(dbDictData);
// 调用
DictDataDO dictData = dictDataService.getDictData(id);
DictDataDO queryData = dictDataService.getDictData(id);
// 断言
assertPojoEquals(dbDictData, dictData);
assertPojoEquals(dbDictData, queryData);
}
@Test
public void testCreateDictData_success() {
// 准备参数
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class,
o -> o.setStatus(randomCommonStatus()))
.setId(null); // 防止 id 被赋值
void testCreateDictData_success() {
// 准备请求对象
DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
reqVO.setDictType("gender");
reqVO.setValue("1");
reqVO.setLabel("");
// mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
Long dictDataId = dictDataService.createDictData(reqVO);
// 断言
assertNotNull(dictDataId);
// 校验记录的属性是否正确
DictDataDO dictData = dictDataMapper.selectById(dictDataId);
assertPojoEquals(reqVO, dictData, "id");
// 模拟 Mapper 插入时生成 ID
doAnswer(invocation -> {
DictDataDO arg = invocation.getArgument(0);
arg.setId(100L); // 模拟自增主键
return null;
}).when(dictDataMapper).insert(any(DictDataDO.class));
// 调用 Service
Long id = dictDataService.createDictData(reqVO);
// 断言返回的 ID 是模拟的
assertEquals(100L, id);
// 验证 Mapper 方法被调用一次,且参数是根据请求对象生成的 DictDataDO
verify(dictDataMapper, times(1)).insert(argThat((DictDataDO dict) ->
"gender".equals(dict.getDictType()) &&
"1".equals(dict.getValue()) &&
"".equals(dict.getLabel())
));
}
@Test
public void testUpdateDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> {
o.setId(dbDictData.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
void testUpdateDictData_success() {
// 准备请求对象
DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
reqVO.setId(100L);
reqVO.setDictType("gender");
reqVO.setValue("1");
reqVO.setLabel("");
// mock 方法,字典类型
when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
// Mock Mapper 的 updateById 方法
when(dictDataMapper.updateById(any(DictDataDO.class))).thenReturn(1);
// 调用 Service
dictDataService.updateDictData(reqVO);
// 校验是否更新正确
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictData);
// 验证 Mapper 被调用
verify(dictDataMapper, times(1)).updateById(argThat((DictDataDO dict) ->
dict.getId().equals(100L) &&
"gender".equals(dict.getDictType()) &&
"1".equals(dict.getValue()) &&
"".equals(dict.getLabel())
));
}
@Test
public void testDeleteDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictData.getId();
void testDeleteDictData_success() {
Long id = 100L;
// mock 方法,字典类型
when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());
// mock mapper 删除行为(deleteById 返回 int
when(dictDataMapper.deleteById(anyLong())).thenReturn(1);
// 调用
dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
// 验证 mapper 被正确调用
verify(dictDataMapper, times(1)).deleteById(id);
}
@Test
public void testValidateDictDataExists_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
void testValidateDictDataExists_success() {
Long id = 1L;
DictDataDO dictData = new DictDataDO();
dictData.setId(id);
// 调用成功
dictDataService.validateDictDataExists(dbDictData.getId());
when(dictDataMapper.selectById(id)).thenReturn(dictData);
dictDataService.validateDictDataExists(id);
verify(dictDataMapper).selectById(id);
}
@Test
public void testValidateDictDataExists_notExists() {
assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
void testValidateDictDataExists_notExists() {
Long id = 1L;
when(dictDataMapper.selectById(id)).thenReturn(null);
// 异常
assertServiceException(() -> dictDataService.validateDictDataExists(id), DICT_DATA_NOT_EXISTS);
}
@Test
public void testValidateDictTypeExists_success() {
// mock 方法,数据类型被禁用
String type = randomString();
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
void testValidateDictDataExists_null_notExists() {
// 不抛异常即可
dictDataService.validateDictDataExists(null);
// 调用, 成功
// mapper 不应被调用
verify(dictDataMapper, never()).selectById(any());
}
@Test
void testValidateDictTypeExists_success() {
String type = "gender";
DictTypeDO dictType = new DictTypeDO();
dictType.setType(type);
dictType.setStatus(CommonStatusEnum.ENABLE.getStatus());
when(dictTypeService.getDictType(type)).thenReturn(dictType);
// 不抛异常即成功
dictDataService.validateDictTypeExists(type);
verify(dictTypeService).getDictType(type);
}
@Test
public void testValidateDictTypeExists_notExists() {
assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS);
void testValidateDictTypeExists_notExists() {
String type = "gender";
when(dictTypeService.getDictType(type)).thenReturn(null);
assertServiceException(() -> dictDataService.validateDictTypeExists(type), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testValidateDictTypeExists_notEnable() {
// mock 方法,数据类型被禁用
String dictType = randomString();
when(dictTypeService.getDictType(eq(dictType))).thenReturn(
randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
void testValidateDictTypeExists_notEnable() {
String type = "gender";
DictTypeDO dictType = new DictTypeDO();
dictType.setType(type);
dictType.setStatus(CommonStatusEnum.DISABLE.getStatus());
when(dictTypeService.getDictType(type)).thenReturn(dictType);
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE);
assertServiceException(() -> dictDataService.validateDictTypeExists(type), DICT_TYPE_NOT_ENABLE);
}
@Test
public void testValidateDictDataValueUnique_success() {
// 调用,成功
dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString());
void testValidateDictDataValueUnique_success() {
DictDataDO exist = new DictDataDO();
exist.setId(10L);
when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
.thenReturn(exist);
dictDataService.validateDictDataValueUnique(10L, "gender", "1");
verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
}
@Test
public void testValidateDictDataValueUnique_valueDuplicateForCreate() {
// 准备参数
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
void testValidateDictDataValueUnique_exist_success() {
when(dictDataMapper.selectByDictTypeAndValue(anyString(), anyString()))
.thenReturn(null);
dictDataService.validateDictDataValueUnique(1L, "gender", "1");
verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
}
@Test
void testValidateDictDataValueUnique_valueDuplicateForCreate() {
DictDataDO exist = new DictDataDO();
exist.setId(10L);
when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
.thenReturn(exist);
// 调用,校验异常
assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value),
assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, "gender", "1"),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
DictDataDO exist = new DictDataDO();
exist.setId(10L);
when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
.thenReturn(exist);
// 调用,校验异常
assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value),
assertServiceException(() -> dictDataService.validateDictDataValueUnique(20L, "gender", "1"),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testGetDictDataCountByDictType() {
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
// 准备参数
String dictType = "yunai";
void testGetDictDataCountByDictType() {
when(dictDataMapper.selectCountByDictType("gender")).thenReturn(5L);
// 调用
long count = dictDataService.getDictDataCountByDictType(dictType);
// 校验
assertEquals(2L, count);
long count = dictDataService.getDictDataCountByDictType("gender");
assertEquals(5L, count);
verify(dictDataMapper).selectCountByDictType("gender");
}
@Test
public void testValidateDictDataList_success() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO);
// 准备参数
String dictType = dictDataDO.getDictType();
List<String> values = singletonList(dictDataDO.getValue());
void testValidateDictDataList_success() {
String dictType = "gender";
List<String> values = List.of("1", "2");
DictDataDO male = new DictDataDO();
male.setValue("1");
male.setStatus(CommonStatusEnum.ENABLE.getStatus());
DictDataDO female = new DictDataDO();
female.setValue("2");
female.setStatus(CommonStatusEnum.ENABLE.getStatus());
when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
.thenReturn(List.of(male, female));
// 调用,无需断言
dictDataService.validateDictDataList(dictType, values);
verify(dictDataMapper).selectByDictTypeAndValues(dictType, values);
}
@Test
public void testValidateDictDataList_notFound() {
// 准备参数
String dictType = randomString();
List<String> values = singletonList(randomString());
void testValidateDictDataList_notFound() {
String dictType = "gender";
List<String> values = List.of("1", "2");
DictDataDO onlyOne = new DictDataDO();
onlyOne.setValue("1");
onlyOne.setStatus(CommonStatusEnum.ENABLE.getStatus());
when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
.thenReturn(List.of(onlyOne));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS);
}
@Test
public void testValidateDictDataList_notEnable() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO);
// 准备参数
String dictType = dictDataDO.getDictType();
List<String> values = singletonList(dictDataDO.getValue());
void testValidateDictDataList_notEnable() {
String dictType = "gender";
List<String> values = List.of("1");
DictDataDO disabled = new DictDataDO();
disabled.setValue("1");
disabled.setStatus(CommonStatusEnum.DISABLE.getStatus());
disabled.setLabel("");
when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
.thenReturn(List.of(disabled));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictDataList(dictType, values),
DICT_DATA_NOT_ENABLE, dictDataDO.getLabel());
DICT_DATA_NOT_ENABLE, "");
}
@Test
public void testGetDictData_dictType() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1");
dictDataMapper.insert(dictDataDO);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2");
dictDataMapper.insert(dictDataDO02);
// 准备参数
String dictType = "yunai";
void testValidateDictDataList_empty() {
dictDataService.validateDictDataList("gender", Collections.emptyList());
verify(dictDataMapper, never())
.selectByDictTypeAndValues(anyString(), anyCollection());
}
@Test
void testGetDictData_dictType() {
String dictType = "gender";
String value = "1";
// 调用
DictDataDO dbDictData = dictDataService.getDictData(dictType, value);
// 断言
assertEquals(dictDataDO, dbDictData);
DictDataDO dictData = new DictDataDO();
dictData.setDictType(dictType);
dictData.setValue(value);
when(dictDataMapper.selectByDictTypeAndValue(dictType, value))
.thenReturn(dictData);
DictDataDO result = dictDataService.getDictData(dictType, value);
assertSame(dictData, result);
verify(dictDataMapper).selectByDictTypeAndValue(dictType, value);
}
@Test
public void testParseDictData() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1");
dictDataMapper.insert(dictDataDO);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2");
dictDataMapper.insert(dictDataDO02);
// 准备参数
String dictType = "yunai";
String label = "1";
void testGetDictI18nData_success() {
String dictType = "gender";
String value = "1";
String language = "en_US";
// 调用
DictDataDO dbDictData = dictDataService.parseDictData(dictType, label);
// 断言
assertEquals(dictDataDO, dbDictData);
DictDataDO dictData = new DictDataDO();
dictData.setDictType(dictType);
dictData.setValue(value);
dictData.setLabel("Male");
when(dictDatai18nMapper
.selectI18nByDictTypeAndValue(dictType, value, language))
.thenReturn(dictData);
DictDataDO result =
dictDataService.getDictI18nData(dictType, value, language);
assertSame(dictData, result);
verify(dictDatai18nMapper)
.selectI18nByDictTypeAndValue(dictType, value, language);
}
// ========== 随机对象 ==========
@Test
void testParseDictData() {
String dictType = "gender";
String label = "";
DictDataDO dictData = new DictDataDO();
dictData.setDictType(dictType);
dictData.setLabel(label);
when(dictDataMapper.selectByDictTypeAndLabel(dictType, label))
.thenReturn(dictData);
DictDataDO result = dictDataService.parseDictData(dictType, label);
assertSame(dictData, result);
verify(dictDataMapper).selectByDictTypeAndLabel(dictType, label);
}
@Test
void testGetI18nVersion_nonEmpty() {
DictDatai18nVersionDO versionDO = new DictDatai18nVersionDO();
versionDO.setVersion(42);
when(dictDataI18nVersionMapper.selectList()).thenReturn(List.of(versionDO));
Integer version = dictDataService.getI18nVersion();
assertEquals(42, version);
verify(dictDataI18nVersionMapper).selectList();
}
@Test
void testGetI18nVersion_emptyList() {
when(dictDataI18nVersionMapper.selectList()).thenReturn(Collections.emptyList());
Integer version = dictDataService.getI18nVersion();
assertEquals(0, version);
verify(dictDataI18nVersionMapper).selectList();
}
@Test
void testUpdateI18nVersion_success() {
// 调用方法
dictDataService.updateI18nVersion();
// 验证 mapper 的 updateVersion 被调用
verify(dictDataI18nVersionMapper).updateVersion();
// 验证 Redis 通知被发送
verify(stringRedisTemplate).convertAndSend(
com.cf.imes.framework.redis.constants.RedisKeyConstants.DICT_DATA_I18N_CACHE_REFRESH, "");
}
// // ========== 随机对象 ==========
//
@SafeVarargs
private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) {
Consumer<DictDataDO> consumer = (o) -> {
@@ -336,6 +513,87 @@ public class DictDataServiceImplTest extends BaseDbUnitTest {
return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers));
}
@Test
void testGetAllLanguageDictData_withCache() {
Map<String, List<DictDataSimpleRespVO>> cache = new HashMap<>();
cache.put("ZH", List.of(new DictDataSimpleRespVO()));
try (MockedStatic<DictDataI18nCacheUtils> mockedCache = Mockito.mockStatic(DictDataI18nCacheUtils.class)) {
mockedCache.when(DictDataI18nCacheUtils::getCache).thenReturn(cache);
Map<String, List<DictDataSimpleRespVO>> result = dictDataService.getAllLanguageDictData();
assertSame(cache, result);
mockedCache.verify(DictDataI18nCacheUtils::getCache, times(1));
}
}
@Test
void testGetAllLanguageDictData_noCache() {
List<DictDataDO> dictDataList = Arrays.asList(new DictDataDO().setId(1L).setDictType("type"));
List<DictDataDO> enList = List.of(new DictDataDO().setId(1L));
try (MockedStatic<DictDataI18nCacheUtils> mockedCache = Mockito.mockStatic(DictDataI18nCacheUtils.class)) {
mockedCache.when(DictDataI18nCacheUtils::getCache).thenReturn(Collections.emptyMap());
when(dictDataMapper.selectListByStatusAndDictType(CommonStatusEnum.ENABLE.getStatus(), null))
.thenReturn(dictDataList);
when(dictDatai18nMapper.getLocaleDictData(List.of(1L), "en")).thenReturn(enList);
Map<String, List<DictDataSimpleRespVO>> result = dictDataService.getAllLanguageDictData();
// 验证中文和英文都存在
assertTrue(result.containsKey("zh"));
assertTrue(result.containsKey("en"));
mockedCache.verify(() -> DictDataI18nCacheUtils.putCache(result), times(1));
}
}
@Test
void testTrans_success() {
DictDataDO dictDataDO1 = new DictDataDO().setId(1L).setLabel("苹果").setStatus(CommonStatusEnum.ENABLE.getStatus());
List<DictDataDO> dictDataList = List.of(dictDataDO1);
// mock 数据库
when(dictDatai18nMapper.delete(any())).thenReturn(1);
when(dictDataMapper.selectList(any())).thenReturn(dictDataList);
// mock TransApi
doReturn("{\"trans_result\":[{\"src\":\"苹果\",\"dst\":\"Apple\"}]}")
.when(transApi).getTransResult(any(), any(), any());
DictDataTransReqVO reqVO = new DictDataTransReqVO();
reqVO.setTargetLanguage("en");
dictDataService.trans(reqVO);
// 验证删除和批量插入被调用
verify(dictDatai18nMapper).delete(any());
verify(dictDatai18nMapper).insertBatch(anyList());
}
@Test
void testTrans_request_error() {
DictDataDO dictDataDO1 = new DictDataDO().setId(1L).setLabel("苹果").setStatus(CommonStatusEnum.ENABLE.getStatus());
List<DictDataDO> dictDataList = List.of(dictDataDO1);
// mock 数据库
when(dictDatai18nMapper.delete(any())).thenReturn(1);
when(dictDataMapper.selectList(any())).thenReturn(dictDataList);
// mock TransApi
doReturn("{\"error_code\":\"52002\"}")
.when(transApi).getTransResult(any(), any(), any());
DictDataTransReqVO reqVO = new DictDataTransReqVO();
reqVO.setTargetLanguage("en");
assertServiceException(() -> dictDataService.trans(reqVO),
DICT_DATA_TRANS_ERROR, "{\"error_code\":\"52002\"}");
}
/**
* 生成一个有效的字典类型
*