mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-29 18:51:53 +08:00
完成 config 模块的单元测试
This commit is contained in:
parent
72817a8632
commit
95857ace9a
@ -2,6 +2,7 @@ package cn.iocoder.dashboard.common.exception.util;
|
|||||||
|
|
||||||
import cn.iocoder.dashboard.common.exception.ErrorCode;
|
import cn.iocoder.dashboard.common.exception.ErrorCode;
|
||||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -91,7 +92,8 @@ public class ServiceExceptionUtil {
|
|||||||
* @param params 参数
|
* @param params 参数
|
||||||
* @return 格式化后的提示
|
* @return 格式化后的提示
|
||||||
*/
|
*/
|
||||||
private static String doFormat(int code, String messagePattern, Object... params) {
|
@VisibleForTesting
|
||||||
|
public static String doFormat(int code, String messagePattern, Object... params) {
|
||||||
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j;
|
int j;
|
||||||
|
@ -13,14 +13,17 @@ import java.io.Serializable;
|
|||||||
@Data
|
@Data
|
||||||
public class PageParam implements Serializable {
|
public class PageParam implements Serializable {
|
||||||
|
|
||||||
|
private static final Integer PAGE_NO = 1;
|
||||||
|
private static final Integer PAGE_SIZE = 10;
|
||||||
|
|
||||||
@ApiModelProperty(value = "页码,从 1 开始", required = true,example = "1")
|
@ApiModelProperty(value = "页码,从 1 开始", required = true,example = "1")
|
||||||
@NotNull(message = "页码不能为空")
|
@NotNull(message = "页码不能为空")
|
||||||
@Min(value = 1, message = "页码最小值为 1")
|
@Min(value = 1, message = "页码最小值为 1")
|
||||||
private Integer pageNo;
|
private Integer pageNo = PAGE_NO;
|
||||||
|
|
||||||
@ApiModelProperty(value = "每页条数,最大值为 100", required = true, example = "10")
|
@ApiModelProperty(value = "每页条数,最大值为 100", required = true, example = "10")
|
||||||
@NotNull(message = "每页条数不能为空")
|
@NotNull(message = "每页条数不能为空")
|
||||||
@Range(min = 1, max = 100, message = "条数范围为 [1, 100]")
|
@Range(min = 1, max = 100, message = "条数范围为 [1, 100]")
|
||||||
private Integer pageSize;
|
private Integer pageSize = PAGE_SIZE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.dashboard.util.date;
|
package cn.iocoder.dashboard.util.date;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,4 +23,40 @@ public class DateUtils {
|
|||||||
return endTime.getTime() - startTime.getTime();
|
return endTime.getTime() - startTime.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定时间
|
||||||
|
*
|
||||||
|
* @param year 年
|
||||||
|
* @param mouth 月
|
||||||
|
* @param day 日
|
||||||
|
* @return 指定时间
|
||||||
|
*/
|
||||||
|
public static Date buildTime(int year, int mouth, int day) {
|
||||||
|
return buildTime(year, mouth, day, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定时间
|
||||||
|
*
|
||||||
|
* @param year 年
|
||||||
|
* @param mouth 月
|
||||||
|
* @param day 日
|
||||||
|
* @param hour 小时
|
||||||
|
* @param minute 分钟
|
||||||
|
* @param second 秒
|
||||||
|
* @return 指定时间
|
||||||
|
*/
|
||||||
|
public static Date buildTime(int year, int mouth, int day,
|
||||||
|
int hour, int minute, int second) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(Calendar.YEAR, year);
|
||||||
|
calendar.set(Calendar.MONTH, mouth - 1);
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
calendar.set(Calendar.MINUTE, minute);
|
||||||
|
calendar.set(Calendar.SECOND, second);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.dashboard.util.object;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object 工具类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class ObjectUtils {
|
||||||
|
|
||||||
|
public static <T> T clone(T object, Consumer<T> consumer) {
|
||||||
|
T result = ObjectUtil.clone(object);
|
||||||
|
if (result != null) {
|
||||||
|
consumer.accept(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,26 +1,31 @@
|
|||||||
package cn.iocoder.dashboard.modules.infra.service.config;
|
package cn.iocoder.dashboard.modules.infra.service.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
|
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
|
||||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||||
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO;
|
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO;
|
||||||
|
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigExportReqVO;
|
||||||
|
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigPageReqVO;
|
||||||
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigUpdateReqVO;
|
import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigUpdateReqVO;
|
||||||
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
|
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
|
||||||
import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper;
|
import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper;
|
||||||
import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
|
import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
|
||||||
import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer;
|
import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer;
|
||||||
import cn.iocoder.dashboard.modules.infra.service.config.impl.InfConfigServiceImpl;
|
import cn.iocoder.dashboard.modules.infra.service.config.impl.InfConfigServiceImpl;
|
||||||
|
import cn.iocoder.dashboard.util.object.ObjectUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||||
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.CONFIG_KEY_DUPLICATE;
|
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*;
|
||||||
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.CONFIG_NOT_EXISTS;
|
|
||||||
import static cn.iocoder.dashboard.util.AssertUtils.assertExceptionEquals;
|
|
||||||
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
|
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
|
||||||
|
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
|
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
|
||||||
|
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@ -36,11 +41,92 @@ public class InfConfigServiceImplTest extends BaseSpringBootUnitTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
private InfConfigProducer configProducer;
|
private InfConfigProducer configProducer;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetConfigPage() {
|
||||||
|
// mock 数据
|
||||||
|
InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到
|
||||||
|
o.setName("芋艿");
|
||||||
|
o.setKey("yunai");
|
||||||
|
o.setType(InfConfigTypeEnum.SYSTEM.getType());
|
||||||
|
o.setCreateTime(buildTime(2021, 2, 1));
|
||||||
|
});
|
||||||
|
configMapper.insert(dbConfig);
|
||||||
|
// 测试 name 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆")));
|
||||||
|
// 测试 key 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou")));
|
||||||
|
// 测试 type 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType())));
|
||||||
|
// 测试 createTime 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
|
||||||
|
// 准备参数
|
||||||
|
InfConfigPageReqVO reqVO = new InfConfigPageReqVO();
|
||||||
|
reqVO.setName("艿");
|
||||||
|
reqVO.setKey("nai");
|
||||||
|
reqVO.setType(InfConfigTypeEnum.SYSTEM.getType());
|
||||||
|
reqVO.setBeginTime(buildTime(2021, 1, 15));
|
||||||
|
reqVO.setEndTime(buildTime(2021, 2, 15));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
PageResult<InfConfigDO> pageResult = configService.getConfigPage(reqVO);
|
||||||
|
// 断言
|
||||||
|
assertEquals(1, pageResult.getTotal());
|
||||||
|
assertEquals(1, pageResult.getList().size());
|
||||||
|
assertPojoEquals(dbConfig, pageResult.getList().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetConfigList() {
|
||||||
|
// mock 数据
|
||||||
|
InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到
|
||||||
|
o.setName("芋艿");
|
||||||
|
o.setKey("yunai");
|
||||||
|
o.setType(InfConfigTypeEnum.SYSTEM.getType());
|
||||||
|
o.setCreateTime(buildTime(2021, 2, 1));
|
||||||
|
});
|
||||||
|
configMapper.insert(dbConfig);
|
||||||
|
// 测试 name 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆")));
|
||||||
|
// 测试 key 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou")));
|
||||||
|
// 测试 type 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType())));
|
||||||
|
// 测试 createTime 不匹配
|
||||||
|
configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
|
||||||
|
// 准备参数
|
||||||
|
InfConfigExportReqVO reqVO = new InfConfigExportReqVO();
|
||||||
|
reqVO.setName("艿");
|
||||||
|
reqVO.setKey("nai");
|
||||||
|
reqVO.setType(InfConfigTypeEnum.SYSTEM.getType());
|
||||||
|
reqVO.setBeginTime(buildTime(2021, 1, 15));
|
||||||
|
reqVO.setEndTime(buildTime(2021, 2, 15));
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
List<InfConfigDO> list = configService.getConfigList(reqVO);
|
||||||
|
// 断言
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
assertPojoEquals(dbConfig, list.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetConfigByKey() {
|
||||||
|
// mock 数据
|
||||||
|
InfConfigDO dbConfig = randomInfConfigDO();
|
||||||
|
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
||||||
|
// 准备参数
|
||||||
|
String key = dbConfig.getKey();
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
InfConfigDO config = configService.getConfigByKey(key);
|
||||||
|
// 断言
|
||||||
|
assertNotNull(config);
|
||||||
|
assertPojoEquals(dbConfig, config);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateConfig_success() {
|
public void testCreateConfig_success() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class);
|
InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class);
|
||||||
// mock
|
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Long configId = configService.createConfig(reqVO);
|
Long configId = configService.createConfig(reqVO);
|
||||||
@ -63,11 +149,8 @@ public class InfConfigServiceImplTest extends BaseSpringBootUnitTest {
|
|||||||
o.setKey(reqVO.getKey()); // 模拟 key 重复
|
o.setKey(reqVO.getKey()); // 模拟 key 重复
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 调用
|
// 调用, 并断言异常
|
||||||
ServiceException serviceException = assertThrows(ServiceException.class,
|
assertServiceException(() -> configService.createConfig(reqVO), CONFIG_KEY_DUPLICATE);
|
||||||
() -> configService.createConfig(reqVO));
|
|
||||||
// 断言异常
|
|
||||||
assertExceptionEquals(CONFIG_KEY_DUPLICATE, serviceException);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -94,20 +177,51 @@ public class InfConfigServiceImplTest extends BaseSpringBootUnitTest {
|
|||||||
// 准备参数
|
// 准备参数
|
||||||
InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class);
|
InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class);
|
||||||
|
|
||||||
|
// 调用, 并断言异常
|
||||||
|
assertServiceException(() -> configService.updateConfig(reqVO), CONFIG_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteConfig_success() {
|
||||||
|
// mock 数据
|
||||||
|
InfConfigDO dbConfig = randomInfConfigDO(o -> {
|
||||||
|
o.setType(InfConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型
|
||||||
|
});
|
||||||
|
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
||||||
|
// 准备参数
|
||||||
|
Long id = dbConfig.getId();
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
ServiceException serviceException = assertThrows(ServiceException.class,
|
configService.deleteConfig(id);
|
||||||
() -> configService.updateConfig(reqVO));
|
// 校验数据不存在了
|
||||||
// 断言异常
|
assertNull(configMapper.selectById(id));
|
||||||
assertExceptionEquals(CONFIG_NOT_EXISTS, serviceException);
|
// 校验调用
|
||||||
|
verify(configProducer, times(1)).sendConfigRefreshMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteConfig_canNotDeleteSystemType() {
|
||||||
|
// mock 数据
|
||||||
|
InfConfigDO dbConfig = randomInfConfigDO(o -> {
|
||||||
|
o.setType(InfConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除
|
||||||
|
});
|
||||||
|
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
||||||
|
// 准备参数
|
||||||
|
Long id = dbConfig.getId();
|
||||||
|
|
||||||
|
// 调用, 并断言异常
|
||||||
|
assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 随机对象 ==========
|
// ========== 随机对象 ==========
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static InfConfigDO randomInfConfigDO(Consumer<InfConfigDO>... consumers) {
|
private static InfConfigDO randomInfConfigDO(Consumer<InfConfigDO>... consumers) {
|
||||||
InfConfigDO config = randomPojo(InfConfigDO.class, consumers);
|
Consumer<InfConfigDO> consumer = (o) -> {
|
||||||
config.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围
|
o.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围
|
||||||
return config;
|
};
|
||||||
|
return randomPojo(InfConfigDO.class, ArrayUtil.append(new Consumer[]{consumer}, consumers));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,15 @@ import cn.hutool.core.util.ArrayUtil;
|
|||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.iocoder.dashboard.common.exception.ErrorCode;
|
import cn.iocoder.dashboard.common.exception.ErrorCode;
|
||||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||||
|
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单元测试,assert 断言工具类
|
* 单元测试,assert 断言工具类
|
||||||
*
|
*
|
||||||
@ -47,14 +51,19 @@ public class AssertUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比对抛出的 ServiceException 是否匹配
|
* 执行方法,校验抛出的 Service 是否符合条件
|
||||||
*
|
*
|
||||||
|
* @param executable 业务异常
|
||||||
* @param errorCode 错误码对象
|
* @param errorCode 错误码对象
|
||||||
* @param serviceException 业务异常
|
* @param messageParams 消息参数
|
||||||
*/
|
*/
|
||||||
public static void assertExceptionEquals(ErrorCode errorCode, ServiceException serviceException) {
|
public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) {
|
||||||
|
// 调用方法
|
||||||
|
ServiceException serviceException = assertThrows(ServiceException.class, executable);
|
||||||
|
// 校验错误码
|
||||||
Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配");
|
Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配");
|
||||||
Assertions.assertEquals(errorCode.getMessage(), serviceException.getMessage(), "错误提示不匹配");
|
String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMessage(), messageParams);
|
||||||
|
Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user