字典数据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); resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver; 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.ErrorCodeConstants;
import com.cf.imes.module.system.enums.common.LanguageEnum; 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.TransApi;
import com.cf.imes.module.system.framework.trans.config.TransProperties;
import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils; import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -77,7 +76,7 @@ public class DictDataServiceImpl implements DictDataService {
private DictDataI18nMapper dictDatai18nMapper; private DictDataI18nMapper dictDatai18nMapper;
@Resource @Resource
private TransProperties transProperties; private TransApi transApi;
@Resource @Resource
private DictDataI18nVersionMapper dictDataI18nVersionMapper; private DictDataI18nVersionMapper dictDataI18nVersionMapper;
@@ -245,7 +244,6 @@ public class DictDataServiceImpl implements DictDataService {
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
// 请求翻译api // 请求翻译api
TransApi transApi = new TransApi(transProperties.getAppId(), transProperties.getSecurityKey(), transProperties.getApiHost());
String transResult = transApi.getTransResult(namesForQuery, "zh", to); String transResult = transApi.getTransResult(namesForQuery, "zh", to);
JSONObject obj = JSON.parseObject(transResult); 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())); () -> deptService.validUserLoginDept(dept.getId()));
assertEquals(ErrorCodeConstants.DEPT_NOT_ALLOWED_LOGIN.getCode(), ex.getCode()); assertEquals(ErrorCodeConstants.DEPT_NOT_ALLOWED_LOGIN.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("测试部-禁用")); assertTrue(ex.getArgs()[0].toString().contains("测试部-禁用"));
} }
@Test @Test
@@ -693,7 +693,7 @@ public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
() -> deptService.validUserLoginDept(child.getId())); () -> deptService.validUserLoginDept(child.getId()));
assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode()); assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("上级部门-禁用")); assertTrue(ex.getArgs()[0].toString().contains("上级部门-禁用"));
} }
@Test @Test
@@ -740,7 +740,7 @@ public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
() -> deptService.validUserLoginDept(child.getId())); () -> deptService.validUserLoginDept(child.getId()));
assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode()); assertEquals(ErrorCodeConstants.DEPT_DISABLE.getCode(), ex.getCode());
assertTrue(ex.getMessage().contains("顶级部门-禁用")); assertTrue(ex.getArgs()[0].toString().contains("顶级部门-禁用"));
} }
@Test @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.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.ArrayUtils; 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.DictDataPageReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; 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.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.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.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.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.mockito.InjectMocks;
import org.springframework.context.annotation.Import; 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.List;
import java.util.Map;
import java.util.function.Consumer; 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.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException; 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.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; 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.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.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 static org.mockito.Mockito.when;
@Import(DictDataServiceImpl.class) class DictDataServiceImplTest extends BaseMockitoUnitTest {
public class DictDataServiceImplTest extends BaseDbUnitTest {
@Resource @InjectMocks
private DictDataServiceImpl dictDataService; private DictDataServiceImpl dictDataService;
@Resource @Mock
private DictDataMapper dictDataMapper; private DictDataMapper dictDataMapper;
@MockBean
@Mock
private DictDataI18nMapper dictDatai18nMapper;
@Mock
private DictDataI18nVersionMapper dictDataI18nVersionMapper;
@Mock
private DictTypeService dictTypeService; private DictTypeService dictTypeService;
@Mock
private StringRedisTemplate stringRedisTemplate;
@Mock
private TransApi transApi;
@Test @Test
public void testGetDictDataList() { void testGetDictDataList() {
// mock 数据 // mock 数据
DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2) DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2)
.setStatus(CommonStatusEnum.ENABLE.getStatus()); .setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO01);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1) DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1)
.setStatus(CommonStatusEnum.ENABLE.getStatus()); .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(); Integer status = CommonStatusEnum.ENABLE.getStatus();
String dictType = "yunai"; String dictType = "yunai";
when(dictDataMapper.selectListByStatusAndDictType(status, dictType)).thenReturn(Arrays.asList(dictDataDO01, dictDataDO02));
// 调用 // 调用
List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType); List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType);
// 断言 // 断言
@@ -66,268 +96,415 @@ public class DictDataServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetDictDataPage() { void testGetDictDataPage() {
// mock 数据 // 准备模拟数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到 DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> {
o.setLabel("晨丰"); o.setLabel("晨丰");
o.setDictType("yunai"); o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}); });
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配 List<DictDataDO> mockList = List.of(dbDictData);
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())));
// 准备参数
DictDataPageReqVO reqVO = new DictDataPageReqVO(); DictDataPageReqVO reqVO = new DictDataPageReqVO();
reqVO.setLabel(""); reqVO.setLabel("");
reqVO.setDictType("yunai"); reqVO.setDictType("yunai");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); 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); PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);
// 断言 // 断言
assertEquals(1, pageResult.getTotal()); assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size()); assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictData, pageResult.getList().get(0)); assertPojoEquals(dbDictData, pageResult.getList().get(0));
// 验证 Mapper 方法被调用
verify(dictDataMapper, times(1)).selectPage(reqVO);
} }
@Test @Test
public void testGetDictData() { void testGetDictData() {
Long id = randomLongId();
// mock 数据 // mock 数据
DictDataDO dbDictData = randomDictDataDO(); DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData); when(dictDataMapper.selectById(id)).thenReturn(dbDictData);
// 准备参数
Long id = dbDictData.getId();
// 调用 // 调用
DictDataDO dictData = dictDataService.getDictData(id); DictDataDO queryData = dictDataService.getDictData(id);
// 断言 // 断言
assertPojoEquals(dbDictData, dictData); assertPojoEquals(dbDictData, queryData);
} }
@Test @Test
public void testCreateDictData_success() { void testCreateDictData_success() {
// 准备参数 // 准备请求对象
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
o -> o.setStatus(randomCommonStatus())) reqVO.setDictType("gender");
.setId(null); // 防止 id 被赋值 reqVO.setValue("1");
reqVO.setLabel("");
// mock 方法 // mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用 // 模拟 Mapper 插入时生成 ID
Long dictDataId = dictDataService.createDictData(reqVO); doAnswer(invocation -> {
// 断言 DictDataDO arg = invocation.getArgument(0);
assertNotNull(dictDataId); arg.setId(100L); // 模拟自增主键
// 校验记录的属性是否正确 return null;
DictDataDO dictData = dictDataMapper.selectById(dictDataId); }).when(dictDataMapper).insert(any(DictDataDO.class));
assertPojoEquals(reqVO, dictData, "id");
// 调用 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 @Test
public void testUpdateDictData_success() { void testUpdateDictData_success() {
// mock 数据 // 准备请求对象
DictDataDO dbDictData = randomDictDataDO(); DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 reqVO.setId(100L);
// 准备参数 reqVO.setDictType("gender");
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> { reqVO.setValue("1");
o.setId(dbDictData.getId()); // 设置更新的 ID reqVO.setLabel("");
o.setStatus(randomCommonStatus());
});
// mock 方法,字典类型 // mock 方法,字典类型
when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); 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); dictDataService.updateDictData(reqVO);
// 校验是否更新正确
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 // 验证 Mapper 被调用
assertPojoEquals(reqVO, dictData); verify(dictDataMapper, times(1)).updateById(argThat((DictDataDO dict) ->
dict.getId().equals(100L) &&
"gender".equals(dict.getDictType()) &&
"1".equals(dict.getValue()) &&
"".equals(dict.getLabel())
));
} }
@Test @Test
public void testDeleteDictData_success() { void testDeleteDictData_success() {
// mock 数据 Long id = 100L;
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 // mock 方法,字典类型
// 准备参数 when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());
Long id = dbDictData.getId();
// mock mapper 删除行为(deleteById 返回 int
when(dictDataMapper.deleteById(anyLong())).thenReturn(1);
// 调用 // 调用
dictDataService.deleteDictData(id); dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id)); // 验证 mapper 被正确调用
verify(dictDataMapper, times(1)).deleteById(id);
} }
@Test @Test
public void testValidateDictDataExists_success() { void testValidateDictDataExists_success() {
// mock 数据 Long id = 1L;
DictDataDO dbDictData = randomDictDataDO(); DictDataDO dictData = new DictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 dictData.setId(id);
// 调用成功 when(dictDataMapper.selectById(id)).thenReturn(dictData);
dictDataService.validateDictDataExists(dbDictData.getId());
dictDataService.validateDictDataExists(id);
verify(dictDataMapper).selectById(id);
} }
@Test @Test
public void testValidateDictDataExists_notExists() { void testValidateDictDataExists_notExists() {
assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); Long id = 1L;
when(dictDataMapper.selectById(id)).thenReturn(null);
// 异常
assertServiceException(() -> dictDataService.validateDictDataExists(id), DICT_DATA_NOT_EXISTS);
} }
@Test @Test
public void testValidateDictTypeExists_success() { void testValidateDictDataExists_null_notExists() {
// mock 方法,数据类型被禁用 // 不抛异常即可
String type = randomString(); dictDataService.validateDictDataExists(null);
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
// 调用, 成功 // 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); dictDataService.validateDictTypeExists(type);
verify(dictTypeService).getDictType(type);
} }
@Test @Test
public void testValidateDictTypeExists_notExists() { void testValidateDictTypeExists_notExists() {
assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS); String type = "gender";
when(dictTypeService.getDictType(type)).thenReturn(null);
assertServiceException(() -> dictDataService.validateDictTypeExists(type), DICT_TYPE_NOT_EXISTS);
} }
@Test @Test
public void testValidateDictTypeExists_notEnable() { void testValidateDictTypeExists_notEnable() {
// mock 方法,数据类型被禁用 String type = "gender";
String dictType = randomString(); DictTypeDO dictType = new DictTypeDO();
when(dictTypeService.getDictType(eq(dictType))).thenReturn( dictType.setType(type);
randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); 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 @Test
public void testValidateDictDataValueUnique_success() { void testValidateDictDataValueUnique_success() {
// 调用,成功 DictDataDO exist = new DictDataDO();
dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString()); exist.setId(10L);
when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
.thenReturn(exist);
dictDataService.validateDictDataValueUnique(10L, "gender", "1");
verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
} }
@Test @Test
public void testValidateDictDataValueUnique_valueDuplicateForCreate() { void testValidateDictDataValueUnique_exist_success() {
// 准备参数 when(dictDataMapper.selectByDictTypeAndValue(anyString(), anyString()))
String dictType = randomString(); .thenReturn(null);
String value = randomString();
// mock 数据 dictDataService.validateDictDataValueUnique(1L, "gender", "1");
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType); verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
o.setValue(value); }
}));
@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); DICT_DATA_VALUE_DUPLICATE);
} }
@Test @Test
public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数 DictDataDO exist = new DictDataDO();
Long id = randomLongId(); exist.setId(10L);
String dictType = randomString();
String value = randomString(); when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
// mock 数据 .thenReturn(exist);
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常 // 调用,校验异常
assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value), assertServiceException(() -> dictDataService.validateDictDataValueUnique(20L, "gender", "1"),
DICT_DATA_VALUE_DUPLICATE); DICT_DATA_VALUE_DUPLICATE);
} }
@Test @Test
public void testGetDictDataCountByDictType() { void testGetDictDataCountByDictType() {
// mock 数据 when(dictDataMapper.selectCountByDictType("gender")).thenReturn(5L);
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
// 准备参数
String dictType = "yunai";
// 调用 long count = dictDataService.getDictDataCountByDictType("gender");
long count = dictDataService.getDictDataCountByDictType(dictType);
// 校验 assertEquals(5L, count);
assertEquals(2L, count); verify(dictDataMapper).selectCountByDictType("gender");
} }
@Test @Test
public void testValidateDictDataList_success() { void testValidateDictDataList_success() {
// mock 数据 String dictType = "gender";
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); List<String> values = List.of("1", "2");
dictDataMapper.insert(dictDataDO);
// 准备参数 DictDataDO male = new DictDataDO();
String dictType = dictDataDO.getDictType(); male.setValue("1");
List<String> values = singletonList(dictDataDO.getValue()); 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); dictDataService.validateDictDataList(dictType, values);
verify(dictDataMapper).selectByDictTypeAndValues(dictType, values);
} }
@Test @Test
public void testValidateDictDataList_notFound() { void testValidateDictDataList_notFound() {
// 准备参数 String dictType = "gender";
String dictType = randomString(); List<String> values = List.of("1", "2");
List<String> values = singletonList(randomString());
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); assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS);
} }
@Test @Test
public void testValidateDictDataList_notEnable() { void testValidateDictDataList_notEnable() {
// mock 数据 String dictType = "gender";
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); List<String> values = List.of("1");
dictDataMapper.insert(dictDataDO);
// 准备参数 DictDataDO disabled = new DictDataDO();
String dictType = dictDataDO.getDictType(); disabled.setValue("1");
List<String> values = singletonList(dictDataDO.getValue()); disabled.setStatus(CommonStatusEnum.DISABLE.getStatus());
disabled.setLabel("");
when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
.thenReturn(List.of(disabled));
// 调用, 并断言异常 // 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), assertServiceException(() -> dictDataService.validateDictDataList(dictType, values),
DICT_DATA_NOT_ENABLE, dictDataDO.getLabel()); DICT_DATA_NOT_ENABLE, "");
} }
@Test @Test
public void testGetDictData_dictType() { void testValidateDictDataList_empty() {
// mock 数据 dictDataService.validateDictDataList("gender", Collections.emptyList());
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1");
dictDataMapper.insert(dictDataDO); verify(dictDataMapper, never())
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2"); .selectByDictTypeAndValues(anyString(), anyCollection());
dictDataMapper.insert(dictDataDO02); }
// 准备参数
String dictType = "yunai"; @Test
void testGetDictData_dictType() {
String dictType = "gender";
String value = "1"; String value = "1";
// 调用 DictDataDO dictData = new DictDataDO();
DictDataDO dbDictData = dictDataService.getDictData(dictType, value); dictData.setDictType(dictType);
// 断言 dictData.setValue(value);
assertEquals(dictDataDO, dbDictData);
when(dictDataMapper.selectByDictTypeAndValue(dictType, value))
.thenReturn(dictData);
DictDataDO result = dictDataService.getDictData(dictType, value);
assertSame(dictData, result);
verify(dictDataMapper).selectByDictTypeAndValue(dictType, value);
} }
@Test @Test
public void testParseDictData() { void testGetDictI18nData_success() {
// mock 数据 String dictType = "gender";
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1"); String value = "1";
dictDataMapper.insert(dictDataDO); String language = "en_US";
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2");
dictDataMapper.insert(dictDataDO02);
// 准备参数
String dictType = "yunai";
String label = "1";
// 调用 DictDataDO dictData = new DictDataDO();
DictDataDO dbDictData = dictDataService.parseDictData(dictType, label); dictData.setDictType(dictType);
// 断言 dictData.setValue(value);
assertEquals(dictDataDO, dbDictData); 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 @SafeVarargs
private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) { private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) {
Consumer<DictDataDO> consumer = (o) -> { Consumer<DictDataDO> consumer = (o) -> {
@@ -336,6 +513,87 @@ public class DictDataServiceImplTest extends BaseDbUnitTest {
return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers)); 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\"}");
}
/** /**
* 生成一个有效的字典类型 * 生成一个有效的字典类型
* *