Merge branch 'master' into feature_user-session-timeout

# Conflicts:
#	sql/ruoyi-vue-pro.sql
This commit is contained in:
Lyon
2021-03-09 10:07:08 +08:00
40 changed files with 631 additions and 258 deletions

View File

@ -0,0 +1,46 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.config.RedisTestConfiguration;
import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB 的单元测试
*
* 注意Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
public class BaseDbAndRedisUnitTest {
@Import({
// DB 配置类
DataSourceConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
MybatisConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
// Redis 配置类
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
RedisAutoConfiguration.class, // Spring Redis 自动配置类
RedisConfig.class, // 自己的 Redis 配置类
RedissonAutoConfiguration.class, // Redisson 自动高配置类
})
public static class Application {
}
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.dashboard;
import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB 的单元测试
*
* 注意Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
public class BaseDbUnitTest {
@Import({
// DB 配置类
DataSourceConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
MybatisConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
})
public static class Application {
}
}

View File

@ -12,6 +12,7 @@ import javax.annotation.Resource;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
@Deprecated
public class BaseSpringBootUnitTest {
@Resource

View File

@ -8,12 +8,10 @@ import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.io.IOException;
@Configuration(proxyBeanMethods = false)
@Lazy(false) // 禁用懒加载,因为需要保证 Redis Server 必须先启动
@EnableConfigurationProperties(RedisProperties.class)
@AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化
public class RedisTestConfiguration {

View File

@ -1,16 +0,0 @@
package cn.iocoder.dashboard.config;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
@Configuration
public class SecurityTestConfiguration {
@Bean
public AuthenticationManager authenticationManager() {
return Mockito.mock(AuthenticationManager.class);
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.infra.service.config;
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
import cn.iocoder.dashboard.BaseDbUnitTest;
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.InfConfigExportReqVO;
@ -15,6 +15,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
@ -24,8 +25,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
@ -36,7 +36,8 @@ import static org.mockito.Mockito.verify;
*
* @author 芋道源码
*/
public class InfConfigServiceTest extends BaseSpringBootUnitTest {
@Import(InfConfigServiceImpl.class)
public class InfConfigServiceTest extends BaseDbUnitTest {
@Resource
private InfConfigServiceImpl configService;
@ -145,19 +146,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testCreateConfig_keyDuplicate() {
// 准备参数
InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class);
// mock 数据
configMapper.insert(randomInfConfigDO(o -> { // @Sql
o.setKey(reqVO.getKey()); // 模拟 key 重复
}));
// 调用, 并断言异常
assertServiceException(() -> configService.createConfig(reqVO), CONFIG_KEY_DUPLICATE);
}
@Test
public void testUpdateConfig_success() {
// mock 数据
@ -177,15 +165,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testUpdateConfig_notExists() {
// 准备参数
InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> configService.updateConfig(reqVO), CONFIG_NOT_EXISTS);
}
@Test
public void testDeleteConfig_success() {
// mock 数据
@ -219,12 +198,49 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
}
@Test
public void testDeleteConfig_notExists() {
public void testCheckConfigExists_success() {
// mock 数据
InfConfigDO dbConfigDO = randomInfConfigDO();
configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据
// 调用成功
configService.checkConfigExists(dbConfigDO.getId());
}
@Test
public void testCheckConfigExist_notExists() {
assertServiceException(() -> configService.checkConfigExists(randomLongId()), CONFIG_NOT_EXISTS);
}
@Test
public void testCheckConfigKeyUnique_success() {
// 调用,成功
configService.checkConfigKeyUnique(randomLongId(), randomString());
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForCreate() {
// 准备参数
String key = randomString();
// mock 数据
configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(null, key),
CONFIG_KEY_DUPLICATE);
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String key = randomString();
// mock 数据
configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
// 调用, 并断言异常
assertServiceException(() -> configService.deleteConfig(id), CONFIG_NOT_EXISTS);
// 调用,校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(id, key),
CONFIG_KEY_DUPLICATE);
}
// ========== 随机对象 ==========

View File

@ -1,15 +1,19 @@
package cn.iocoder.dashboard.modules.system.service.auth;
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.framework.security.core.LoginUser;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl;
import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
import cn.iocoder.dashboard.util.AssertUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
@ -21,7 +25,13 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class SysAuthServiceImplTest extends BaseSpringBootUnitTest {
/**
* {@link SysAuthServiceImpl} 的单元测试
*
* @author 芋道源码
*/
@Import(SysAuthServiceImpl.class)
public class SysAuthServiceImplTest extends BaseDbUnitTest {
@Resource
private SysAuthServiceImpl authService;
@ -30,6 +40,14 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest {
private SysUserService userService;
@MockBean
private SysPermissionService permissionService;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private SysCaptchaService captchaService;
@MockBean
private SysLoginLogService loginLogService;
@MockBean
private SysUserSessionService userSessionService;
@Test
public void testLoadUserByUsername_success() {

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.system.service.dict;
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO;
@ -12,14 +12,20 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper;
import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer;
import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl;
import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.ImmutableTable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.*;
@ -32,7 +38,8 @@ import static org.mockito.Mockito.*;
*
* @author 芋道源码
*/
public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
@Import(SysDictDataServiceImpl.class)
public class SysDictDataServiceTest extends BaseDbUnitTest {
@Resource
private SysDictDataServiceImpl dictDataService;
@ -44,6 +51,37 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
@MockBean
private SysDictDataProducer dictDataProducer;
/**
* 测试加载到新的字典数据的情况
*/
@Test
@SuppressWarnings("unchecked")
public void testInitLocalCache() {
// mock 数据
SysDictDataDO dictData01 = randomDictDataDO();
dictDataMapper.insert(dictData01);
SysDictDataDO dictData02 = randomDictDataDO();
dictDataMapper.insert(dictData02);
// 调用
dictDataService.initLocalCache();
// 断言 labelDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> labelDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "labelDictDataCache");
assertEquals(2, labelDictDataCache.size());
assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel()));
assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel()));
// 断言 valueDictDataCache 缓存
ImmutableTable<String, String, SysDictDataDO> valueDictDataCache =
(ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "valueDictDataCache");
assertEquals(2, valueDictDataCache.size());
assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue()));
assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime");
assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime);
}
@Test
public void testGetDictDataPage() {
// mock 数据
@ -107,8 +145,7 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType())))
.thenReturn(randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
Long dictDataId = dictDataService.createDictData(reqVO);
@ -124,50 +161,142 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
@Test
public void testUpdateDictData_success() {
// mock 数据
SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class);
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> {
o.setId(dbDictData.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// mock 方法,字典类型
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
dictDataService.updateDictData(reqVO);
// 校验是否更新正确
SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictData);
}
@Test
public void testUpdateDictData_notExists() {
// 准备参数
SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_NOT_EXISTS);
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testDeleteDictData_success() {
// mock 数据
SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class);
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictData.getId();
// 调用
dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testDeleteDictData_notExists() {
// 准备参数
Long id = randomLongId();
public void testCheckDictDataExists_success() {
// mock 数据
SysDictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 调用成功
dictDataService.checkDictDataExists(dbDictData.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_success() {
// mock 方法,数据类型被禁用
String type = randomString();
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
// 调用, 成功
dictDataService.checkDictTypeValid(type);
}
@Test
public void testCheckDictTypeValid_notExists() {
assertServiceException(() -> dictDataService.checkDictTypeValid(randomString()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_notEnable() {
// mock 方法,数据类型被禁用
String dictType = randomString();
when(dictTypeService.getDictType(eq(dictType))).thenReturn(
randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.deleteDictData(id), DICT_DATA_NOT_EXISTS);
assertServiceException(() -> dictDataService.checkDictTypeValid(dictType), DICT_TYPE_NOT_ENABLE);
}
@Test
public void testCheckDictDataValueUnique_success() {
// 调用,成功
dictDataService.checkDictDataValueUnique(randomLongId(), randomString(), randomString());
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForCreate() {
// 准备参数
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(null, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(id, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) {
Consumer<SysDictDataDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers));
}
/**
* 生成一个有效的字典类型
*
* @param type 字典类型
* @return SysDictTypeDO 对象
*/
private static SysDictTypeDO randomDictTypeDO(String type) {
return randomPojo(SysDictTypeDO.class, o -> {
o.setType(type);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
});
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.modules.system.service.dict;
import cn.iocoder.dashboard.BaseSpringBootUnitTest;
import cn.iocoder.dashboard.BaseDbUnitTest;
import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO;
@ -14,6 +14,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
@ -23,8 +24,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
import static cn.iocoder.dashboard.util.RandomUtils.*;
import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
@ -35,7 +35,8 @@ import static org.mockito.Mockito.when;
*
* @author 芋道源码
*/
public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
@Import(SysDictTypeServiceImpl.class)
public class SysDictTypeServiceTest extends BaseDbUnitTest {
@Resource
private SysDictTypeServiceImpl dictTypeService;
@ -142,32 +143,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
assertPojoEquals(reqVO, dictType);
}
@Test
public void testCreateDictType_nameDuplicate() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class,
o -> o.setName(dbDictType.getName())); // 模拟 name 重复
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testCreateDictType_typeDuplicate() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class,
o -> o.setType(dbDictType.getType())); // 模拟 type 重复
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testUpdateDictType_success() {
// mock 数据
@ -186,33 +161,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
assertPojoEquals(reqVO, dictType);
}
@Test
public void testUpdateDictType_notExists() {
// 准备参数
SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testUpdateDictType_nameDuplicate() {
// mock 数据,稍后更新它
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);
// mock 数据ks稍后模拟重复它的名字
SysDictTypeDO nameDictType = randomDictTypeDO();
dictTypeMapper.insert(nameDictType);
// 准备参数
SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class, o -> {
o.setId(dbDictType.getId()); // 设置更新的 ID
o.setName(nameDictType.getName()); // 模拟 name 重复
});
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testDeleteDictType_success() {
// mock 数据
@ -227,15 +175,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
assertNull(dictTypeMapper.selectById(id));
}
@Test
public void testDeleteDictType_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testDeleteDictType_hasChildren() {
// mock 数据
@ -250,6 +189,83 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN);
}
@Test
public void testCheckDictDataExists_success() {
// mock 数据
SysDictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 调用成功
dictTypeService.checkDictTypeExists(dbDictType.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictTypeService.checkDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForCreate() {
// 准备参数
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(null, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(id, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypNameUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeNameUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForCreate() {
// 准备参数
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name),
DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name),
DICT_TYPE_NAME_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs

View File

@ -3,21 +3,6 @@ spring:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
# 去除的自动配置项
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration # 单元测试,禁用 SpringSecurity
- org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration # 单元测试,禁用 SpringSecurity
- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 单元测试,禁用 Quartz
- com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration # 单元测试,禁用 Lock4j 分布式锁
- org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration # 单元测试,禁用 Scheduler 定时任务
- org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration # 项目没有使用 Spring Data所以禁用配置类加速启动
# Swagger 接口文档的自动配置(单元测试,禁用 Swagger)
springfox:
documentation:
auto-startup: false
--- #################### 数据库相关配置 ####################
spring:
@ -29,6 +14,9 @@ spring:
username: sa
password:
schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
@ -36,17 +24,13 @@ spring:
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 定时任务相关配置 ####################
# Quartz 配置项,对应 QuartzProperties 配置类(单元测试,禁用 Quartz
--- #################### 配置中心相关配置 ####################
# Apollo 配置中心
apollo:
bootstrap:
enabled: false # 单元测试,禁用配置中心
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
@ -63,23 +47,6 @@ resilience4j:
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
enabled-by-default: false
# Spring Boot Admin 配置项
spring:
boot:
admin:
# Spring Boot Admin Client 客户端的相关配置
client:
enabled: false
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring
# 日志文件配置(不需要配置)
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置

View File

@ -9,9 +9,9 @@ CREATE TABLE IF NOT EXISTS "inf_config" (
"value" varchar(500) NOT NULL DEFAULT '',
"sensitive" bit NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS "sys_dept" (
"phone" varchar(11) DEFAULT NULL,
"email" varchar(50) DEFAULT NULL,
"status" tinyint NOT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -44,9 +44,9 @@ CREATE TABLE IF NOT EXISTS "sys_dict_data" (
"dict_type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"remark" varchar(500) DEFAULT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -62,9 +62,9 @@ CREATE TABLE IF NOT EXISTS "sys_role" (
"status" tinyint NOT NULL,
"type" tinyint NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -74,9 +74,9 @@ CREATE TABLE IF NOT EXISTS "sys_role_menu" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"role_id" bigint NOT NULL,
"menu_id" bigint NOT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -93,9 +93,9 @@ CREATE TABLE IF NOT EXISTS "sys_menu" (
"icon" varchar(100) DEFAULT '#',
"component" varchar(255) DEFAULT NULL,
"status" tinyint NOT NULL DEFAULT '0',
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
@ -107,9 +107,9 @@ CREATE TABLE "sys_dict_type" (
"type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"remark" varchar(500) DEFAULT NULL,
"create_by" varchar(64) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"update_by" varchar(64) DEFAULT '',
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")