Commit 113e34b2791ec9b54f5c2cd915890057454d92ed
1 parent
cafb72b5
完善 dict data 的单元测试
Showing
4 changed files
with
150 additions
and
22 deletions
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java
| ... | ... | @@ -15,9 +15,9 @@ import java.util.List; |
| 15 | 15 | @Mapper |
| 16 | 16 | public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> { |
| 17 | 17 | |
| 18 | - default SysDictDataDO selectByDictTypeAndLabel(String dictType, String label) { | |
| 18 | + default SysDictDataDO selectByDictTypeAndLabel(String dictType, String value) { | |
| 19 | 19 | return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType) |
| 20 | - .eq("label", label)); | |
| 20 | + .eq("value", value)); | |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | default int selectCountByDictType(String dictType) { | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java
| ... | ... | @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.dict.impl; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.iocoder.dashboard.common.enums.CommonStatusEnum; |
| 5 | -import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | |
| 6 | 5 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 7 | 6 | import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; |
| 8 | 7 | import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO; |
| ... | ... | @@ -10,9 +9,9 @@ import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataEx |
| 10 | 9 | import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataPageReqVO; |
| 11 | 10 | import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO; |
| 12 | 11 | import cn.iocoder.dashboard.modules.system.convert.dict.SysDictDataConvert; |
| 13 | -import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; | |
| 14 | 12 | import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictDataDO; |
| 15 | 13 | import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; |
| 14 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; | |
| 16 | 15 | import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; |
| 17 | 16 | import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; |
| 18 | 17 | import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; |
| ... | ... | @@ -28,6 +27,7 @@ import java.util.Comparator; |
| 28 | 27 | import java.util.Date; |
| 29 | 28 | import java.util.List; |
| 30 | 29 | |
| 30 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 31 | 31 | import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; |
| 32 | 32 | |
| 33 | 33 | /** |
| ... | ... | @@ -156,7 +156,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { |
| 156 | 156 | @Override |
| 157 | 157 | public Long createDictData(SysDictDataCreateReqVO reqVO) { |
| 158 | 158 | // 校验正确性 |
| 159 | - this.checkCreateOrUpdate(null, reqVO.getLabel(), reqVO.getDictType()); | |
| 159 | + this.checkCreateOrUpdate(null, reqVO.getValue(), reqVO.getDictType()); | |
| 160 | 160 | // 插入字典类型 |
| 161 | 161 | SysDictDataDO dictData = SysDictDataConvert.INSTANCE.convert(reqVO); |
| 162 | 162 | dictDataMapper.insert(dictData); |
| ... | ... | @@ -168,7 +168,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { |
| 168 | 168 | @Override |
| 169 | 169 | public void updateDictData(SysDictDataUpdateReqVO reqVO) { |
| 170 | 170 | // 校验正确性 |
| 171 | - this.checkCreateOrUpdate(reqVO.getId(), reqVO.getLabel(), reqVO.getDictType()); | |
| 171 | + this.checkCreateOrUpdate(reqVO.getId(), reqVO.getValue(), reqVO.getDictType()); | |
| 172 | 172 | // 更新字典类型 |
| 173 | 173 | SysDictDataDO updateObj = SysDictDataConvert.INSTANCE.convert(reqVO); |
| 174 | 174 | dictDataMapper.updateById(updateObj); |
| ... | ... | @@ -191,13 +191,13 @@ public class SysDictDataServiceImpl implements SysDictDataService { |
| 191 | 191 | return dictDataMapper.selectCountByDictType(dictType); |
| 192 | 192 | } |
| 193 | 193 | |
| 194 | - private void checkCreateOrUpdate(Long id, String label, String dictType) { | |
| 194 | + private void checkCreateOrUpdate(Long id, String value, String dictType) { | |
| 195 | 195 | // 校验自己存在 |
| 196 | 196 | checkDictDataExists(id); |
| 197 | 197 | // 校验字典类型有效 |
| 198 | 198 | checkDictTypeValid(dictType); |
| 199 | 199 | // 校验字典数据的值的唯一性 |
| 200 | - checkDictDataValueUnique(id, dictType, label); | |
| 200 | + checkDictDataValueUnique(id, dictType, value); | |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | 203 | private void checkDictDataValueUnique(Long id, String dictType, String label) { |
| ... | ... | @@ -207,10 +207,10 @@ public class SysDictDataServiceImpl implements SysDictDataService { |
| 207 | 207 | } |
| 208 | 208 | // 如果 id 为空,说明不用比较是否为相同 id 的字典数据 |
| 209 | 209 | if (id == null) { |
| 210 | - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); | |
| 210 | + throw exception(DICT_DATA_VALUE_DUPLICATE); | |
| 211 | 211 | } |
| 212 | 212 | if (!dictData.getId().equals(id)) { |
| 213 | - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); | |
| 213 | + throw exception(DICT_DATA_VALUE_DUPLICATE); | |
| 214 | 214 | } |
| 215 | 215 | } |
| 216 | 216 | |
| ... | ... | @@ -220,17 +220,17 @@ public class SysDictDataServiceImpl implements SysDictDataService { |
| 220 | 220 | } |
| 221 | 221 | SysDictDataDO dictData = dictDataMapper.selectById(id); |
| 222 | 222 | if (dictData == null) { |
| 223 | - throw ServiceExceptionUtil.exception(DICT_DATA_NOT_EXISTS); | |
| 223 | + throw exception(DICT_DATA_NOT_EXISTS); | |
| 224 | 224 | } |
| 225 | 225 | } |
| 226 | 226 | |
| 227 | 227 | private void checkDictTypeValid(String type) { |
| 228 | 228 | SysDictTypeDO dictType = dictTypeService.getDictType(type); |
| 229 | 229 | if (dictType == null) { |
| 230 | - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_EXISTS); | |
| 230 | + throw exception(DICT_TYPE_NOT_EXISTS); | |
| 231 | 231 | } |
| 232 | 232 | if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) { |
| 233 | - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_ENABLE); | |
| 233 | + throw exception(DICT_TYPE_NOT_ENABLE); | |
| 234 | 234 | } |
| 235 | 235 | } |
| 236 | 236 | ... | ... |
src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java
| ... | ... | @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.ArrayUtil; |
| 4 | 4 | |
| 5 | +import java.util.function.Consumer; | |
| 6 | + | |
| 5 | 7 | /** |
| 6 | 8 | * Array 工具类 |
| 7 | 9 | * |
| ... | ... | @@ -18,11 +20,11 @@ public class ArrayUtils { |
| 18 | 20 | * @return 结果数组 |
| 19 | 21 | */ |
| 20 | 22 | @SafeVarargs |
| 21 | - public static <T> T[] append(T object, T... newElements) { | |
| 23 | + public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) { | |
| 22 | 24 | if (object == null) { |
| 23 | 25 | return newElements; |
| 24 | 26 | } |
| 25 | - T[] result = ArrayUtil.newArray(object.getClass(), 1 + newElements.length); | |
| 27 | + Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); | |
| 26 | 28 | result[0] = object; |
| 27 | 29 | System.arraycopy(newElements, 0, result, 1, newElements.length); |
| 28 | 30 | return result; | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java
| ... | ... | @@ -12,14 +12,16 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; |
| 12 | 12 | import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; |
| 13 | 13 | import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; |
| 14 | 14 | import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl; |
| 15 | +import cn.iocoder.dashboard.util.collection.ArrayUtils; | |
| 15 | 16 | import cn.iocoder.dashboard.util.object.ObjectUtils; |
| 16 | 17 | import org.junit.jupiter.api.Test; |
| 17 | 18 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 18 | 19 | |
| 19 | 20 | import javax.annotation.Resource; |
| 20 | 21 | import java.util.List; |
| 22 | +import java.util.function.Consumer; | |
| 21 | 23 | |
| 22 | -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS; | |
| 24 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 23 | 25 | import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; |
| 24 | 26 | import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; |
| 25 | 27 | import static cn.iocoder.dashboard.util.RandomUtils.*; |
| ... | ... | @@ -107,8 +109,7 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { |
| 107 | 109 | SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, |
| 108 | 110 | o -> o.setStatus(randomCommonStatus())); |
| 109 | 111 | // mock 方法 |
| 110 | - when(dictTypeService.getDictType(eq(reqVO.getDictType()))) | |
| 111 | - .thenReturn(randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); | |
| 112 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | |
| 112 | 113 | |
| 113 | 114 | // 调用 |
| 114 | 115 | Long dictDataId = dictDataService.createDictData(reqVO); |
| ... | ... | @@ -122,20 +123,65 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { |
| 122 | 123 | } |
| 123 | 124 | |
| 124 | 125 | @Test |
| 126 | + public void testCreateDictData_dictTypeNotExists() { | |
| 127 | + // 准备参数 | |
| 128 | + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, | |
| 129 | + o -> o.setStatus(randomCommonStatus())); | |
| 130 | + | |
| 131 | + // 调用, 并断言异常 | |
| 132 | + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_EXISTS); | |
| 133 | + } | |
| 134 | + | |
| 135 | + @Test | |
| 136 | + public void testCreateDictData_dictTypeNotEnable() { | |
| 137 | + // 准备参数 | |
| 138 | + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, | |
| 139 | + o -> o.setStatus(randomCommonStatus())); | |
| 140 | + // mock 方法,数据类型被禁用 | |
| 141 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( | |
| 142 | + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 143 | + | |
| 144 | + // 调用, 并断言异常 | |
| 145 | + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_ENABLE); | |
| 146 | + } | |
| 147 | + | |
| 148 | + @Test | |
| 149 | + public void testCreateDictData_dictDataValueDuplicate() { | |
| 150 | + // 准备参数 | |
| 151 | + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, | |
| 152 | + o -> o.setStatus(randomCommonStatus())); | |
| 153 | + // mock 方法,字典类型 | |
| 154 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | |
| 155 | + // mock dictData 重复 value 重复 | |
| 156 | + dictDataMapper.insert(randomDictDataDO(o -> { | |
| 157 | + o.setDictType(reqVO.getDictType()); | |
| 158 | + o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 | |
| 159 | + })); | |
| 160 | + | |
| 161 | + // 调用, 并断言异常 | |
| 162 | + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); | |
| 163 | + } | |
| 164 | + | |
| 165 | + @Test | |
| 125 | 166 | public void testUpdateDictData_success() { |
| 126 | 167 | // mock 数据 |
| 127 | - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); | |
| 168 | + SysDictDataDO dbDictData = randomDictDataDO(); | |
| 128 | 169 | dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 |
| 129 | 170 | // 准备参数 |
| 130 | 171 | SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { |
| 131 | 172 | o.setId(dbDictData.getId()); // 设置更新的 ID |
| 173 | + o.setStatus(randomCommonStatus()); | |
| 132 | 174 | }); |
| 175 | + // mock 方法,字典类型 | |
| 176 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | |
| 133 | 177 | |
| 134 | 178 | // 调用 |
| 135 | 179 | dictDataService.updateDictData(reqVO); |
| 136 | 180 | // 校验是否更新正确 |
| 137 | 181 | SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 |
| 138 | 182 | assertPojoEquals(reqVO, dictData); |
| 183 | + // 校验调用 | |
| 184 | + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); | |
| 139 | 185 | } |
| 140 | 186 | |
| 141 | 187 | @Test |
| ... | ... | @@ -148,17 +194,74 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { |
| 148 | 194 | } |
| 149 | 195 | |
| 150 | 196 | @Test |
| 197 | + public void testUpdateDictData_dictTypeNotExists() { | |
| 198 | + // mock 数据 | |
| 199 | + SysDictDataDO dbDictData = randomDictDataDO(); | |
| 200 | + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | |
| 201 | + // 准备参数 | |
| 202 | + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { | |
| 203 | + o.setId(dbDictData.getId()); // 设置更新的 ID | |
| 204 | + o.setStatus(randomCommonStatus()); | |
| 205 | + }); | |
| 206 | + | |
| 207 | + // 调用, 并断言异常 | |
| 208 | + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_EXISTS); | |
| 209 | + } | |
| 210 | + | |
| 211 | + @Test | |
| 212 | + public void testUpdateDictData_dictTypeNotEnable() { | |
| 213 | + // mock 数据 | |
| 214 | + SysDictDataDO dbDictData = randomDictDataDO(); | |
| 215 | + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | |
| 216 | + // 准备参数 | |
| 217 | + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { | |
| 218 | + o.setId(dbDictData.getId()); // 设置更新的 ID | |
| 219 | + o.setStatus(randomCommonStatus()); | |
| 220 | + }); | |
| 221 | + // mock 方法,数据类型被禁用 | |
| 222 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( | |
| 223 | + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 224 | + | |
| 225 | + // 调用, 并断言异常 | |
| 226 | + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_ENABLE); | |
| 227 | + } | |
| 228 | + | |
| 229 | + @Test | |
| 230 | + public void testUpdateDictData_dictDataValueDuplicate() { | |
| 231 | + // mock 数据 | |
| 232 | + SysDictDataDO dbDictData = randomDictDataDO(); | |
| 233 | + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | |
| 234 | + // 准备参数 | |
| 235 | + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { | |
| 236 | + o.setId(dbDictData.getId()); // 设置更新的 ID | |
| 237 | + o.setStatus(randomCommonStatus()); | |
| 238 | + }); | |
| 239 | + // mock 方法,字典类型 | |
| 240 | + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | |
| 241 | + // mock dictData 重复 value 重复 | |
| 242 | + dictDataMapper.insert(randomDictDataDO(o -> { | |
| 243 | + o.setDictType(reqVO.getDictType()); | |
| 244 | + o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 | |
| 245 | + })); | |
| 246 | + | |
| 247 | + // 调用, 并断言异常 | |
| 248 | + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); | |
| 249 | + } | |
| 250 | + | |
| 251 | + @Test | |
| 151 | 252 | public void testDeleteDictData_success() { |
| 152 | 253 | // mock 数据 |
| 153 | - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); | |
| 254 | + SysDictDataDO dbDictData = randomDictDataDO(); | |
| 154 | 255 | dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 |
| 155 | 256 | // 准备参数 |
| 156 | 257 | Long id = dbDictData.getId(); |
| 157 | 258 | |
| 158 | 259 | // 调用 |
| 159 | 260 | dictDataService.deleteDictData(id); |
| 160 | - // 校验数据不存在了 | |
| 161 | - assertNull(dictDataMapper.selectById(id)); | |
| 261 | + // 校验数据不存在了 | |
| 262 | + assertNull(dictDataMapper.selectById(id)); | |
| 263 | + // 校验调用 | |
| 264 | + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); | |
| 162 | 265 | } |
| 163 | 266 | |
| 164 | 267 | @Test |
| ... | ... | @@ -170,4 +273,27 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { |
| 170 | 273 | assertServiceException(() -> dictDataService.deleteDictData(id), DICT_DATA_NOT_EXISTS); |
| 171 | 274 | } |
| 172 | 275 | |
| 276 | + // ========== 随机对象 ========== | |
| 277 | + | |
| 278 | + @SafeVarargs | |
| 279 | + private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) { | |
| 280 | + Consumer<SysDictDataDO> consumer = (o) -> { | |
| 281 | + o.setStatus(randomCommonStatus()); // 保证 status 的范围 | |
| 282 | + }; | |
| 283 | + return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers)); | |
| 284 | + } | |
| 285 | + | |
| 286 | + /** | |
| 287 | + * 生成一个有效的字典类型 | |
| 288 | + * | |
| 289 | + * @param type 字典类型 | |
| 290 | + * @return SysDictTypeDO 对象 | |
| 291 | + */ | |
| 292 | + private static SysDictTypeDO randomDictTypeDO(String type) { | |
| 293 | + return randomPojo(SysDictTypeDO.class, o -> { | |
| 294 | + o.setType(type); | |
| 295 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 | |
| 296 | + }); | |
| 297 | + } | |
| 298 | + | |
| 173 | 299 | } | ... | ... |