Commit 2f0d7e8aba72d8239c597bad6e27a84477ce5272

Authored by YunaiV
1 parent 99855e7c

单元测试,接入 h2 实现单元测试

... ... @@ -177,6 +177,12 @@
177 177 <scope>test</scope>
178 178 </dependency>
179 179  
  180 + <dependency>
  181 + <groupId>com.h2database</groupId> <!-- 单元测试,我们采用 H2 作为数据库 -->
  182 + <artifactId>h2</artifactId>
  183 + <scope>test</scope>
  184 + </dependency>
  185 +
180 186 <!-- 工具类相关 -->
181 187 <dependency>
182 188 <groupId>org.projectlombok</groupId>
... ...
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
... ... @@ -27,7 +27,7 @@ public class RedisConfig {
27 27 template.setConnectionFactory(factory);
28 28 // 使用 String 序列化方式,序列化 KEY 。
29 29 template.setKeySerializer(RedisSerializer.string());
30   - // 使用 JSON 序列化方式(库是 FastJSON ),序列化 VALUE 。
  30 + // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
31 31 template.setValueSerializer(RedisSerializer.json());
32 32 return template;
33 33 }
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/enums/InfErrorCodeConstants.java
... ... @@ -11,7 +11,7 @@ public interface InfErrorCodeConstants {
11 11  
12 12 // ========== 参数配置 1001000000 ==========
13 13 ErrorCode CONFIG_NOT_FOUND = new ErrorCode(1001000001, "参数配置不存在");
14   - ErrorCode CONFIG_NAME_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复");
  14 + ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复");
15 15 ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置");
16 16 ErrorCode CONFIG_GET_VALUE_ERROR_IF_SENSITIVE = new ErrorCode(1001000004, "不允许获取敏感配置到前端");
17 17  
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java
... ... @@ -117,10 +117,10 @@ public class InfConfigServiceImpl implements InfConfigService {
117 117 }
118 118 // 如果 id 为空,说明不用比较是否为相同 id 的参数配置
119 119 if (id == null) {
120   - throw ServiceExceptionUtil.exception(CONFIG_NAME_DUPLICATE);
  120 + throw ServiceExceptionUtil.exception(CONFIG_KEY_DUPLICATE);
121 121 }
122 122 if (!config.getId().equals(id)) {
123   - throw ServiceExceptionUtil.exception(CONFIG_NAME_DUPLICATE);
  123 + throw ServiceExceptionUtil.exception(CONFIG_KEY_DUPLICATE);
124 124 }
125 125 }
126 126  
... ...
src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.infra.service.config;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ServiceException;
  4 +import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO;
  5 +import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
  6 +import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper;
  7 +import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
  8 +import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer;
  9 +import cn.iocoder.dashboard.modules.infra.service.config.impl.InfConfigServiceImpl;
  10 +import cn.iocoder.dashboard.util.AssertUtils;
  11 +import org.junit.jupiter.api.Test;
  12 +import org.springframework.boot.test.context.SpringBootTest;
  13 +import org.springframework.boot.test.mock.mockito.MockBean;
  14 +import org.springframework.test.context.ActiveProfiles;
  15 +import org.springframework.test.context.jdbc.Sql;
  16 +
  17 +import javax.annotation.Resource;
  18 +
  19 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.CONFIG_KEY_DUPLICATE;
  20 +import static org.junit.jupiter.api.Assertions.*;
  21 +import static org.mockito.Mockito.times;
  22 +import static org.mockito.Mockito.verify;
  23 +
  24 +@SpringBootTest
  25 +@ActiveProfiles("unit-test")
  26 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
  27 +public class InfConfigServiceImplTest {
  28 +
  29 + @Resource
  30 + private InfConfigServiceImpl configService;
  31 +
  32 + @Resource
  33 + private InfConfigMapper configMapper;
  34 +
  35 + @MockBean
  36 + private InfConfigProducer configProducer;
  37 +
  38 + @Test
  39 + public void testCreateConfig_success() {
  40 + // 入参
  41 + InfConfigCreateReqVO reqVO = new InfConfigCreateReqVO();
  42 + reqVO.setGroup("test_group");
  43 + reqVO.setName("test_name");
  44 + reqVO.setValue("test_value");
  45 + reqVO.setSensitive(true);
  46 + reqVO.setRemark("test_remark");
  47 + reqVO.setKey("test_key");
  48 + // mock
  49 +
  50 + // 调用
  51 + Long configId = configService.createConfig(reqVO);
  52 + // 校验
  53 + assertNotNull(configId);
  54 + // 校验记录的属性是否正确
  55 + InfConfigDO config = configMapper.selectById(configId);
  56 + AssertUtils.assertEquals(reqVO, config);
  57 + assertEquals(InfConfigTypeEnum.CUSTOM.getType(), config.getType());
  58 + // 校验调用
  59 + verify(configProducer, times(1)).sendConfigRefreshMessage();
  60 + }
  61 +
  62 + @Test
  63 + @Sql(statements = "INSERT INTO `inf_config`(`group`, `type`, `name`, `key`, `value`, `sensitive`) VALUES ('test_group', 1, 'test_name', 'test_key', 'test_value', 1);")
  64 + public void testCreateConfig_keyDuplicate() {
  65 + // 入参
  66 + InfConfigCreateReqVO reqVO = new InfConfigCreateReqVO();
  67 + reqVO.setGroup("test_group");
  68 + reqVO.setName("test_name");
  69 + reqVO.setValue("test_value");
  70 + reqVO.setSensitive(true);
  71 + reqVO.setRemark("test_remark");
  72 + reqVO.setKey("test_key");
  73 + // mock
  74 +
  75 + // 调用
  76 + ServiceException serviceException = assertThrows(ServiceException.class, () -> configService.createConfig(reqVO));
  77 + // 断言
  78 + AssertUtils.assertEquals(CONFIG_KEY_DUPLICATE, serviceException);
  79 + }
  80 +
  81 +}
... ...
src/test/java/cn/iocoder/dashboard/modules/tool/dal/mysql/coegen/ToolInformationSchemaColumnMapperTest.java
... ... @@ -3,19 +3,23 @@ package cn.iocoder.dashboard.modules.tool.dal.mysql.coegen;
3 3 import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaColumnDO;
4 4 import org.junit.jupiter.api.Test;
5 5 import org.springframework.boot.test.context.SpringBootTest;
  6 +import org.springframework.test.context.ActiveProfiles;
  7 +import org.springframework.test.context.jdbc.Sql;
6 8  
7 9 import javax.annotation.Resource;
8 10 import java.util.List;
9 11  
10   -import static org.junit.jupiter.api.Assertions.*;
  12 +import static org.junit.jupiter.api.Assertions.assertTrue;
11 13  
12 14 @SpringBootTest
  15 +@ActiveProfiles("unit-test")
13 16 public class ToolInformationSchemaColumnMapperTest {
14 17  
15 18 @Resource
16 19 private ToolSchemaColumnMapper toolInformationSchemaColumnMapper;
17 20  
18 21 @Test
  22 + @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
19 23 public void testSelectListByTableName() {
20 24 List<ToolSchemaColumnDO> columns = toolInformationSchemaColumnMapper
21 25 .selectListByTableName("inf_config");
... ...
src/test/java/cn/iocoder/dashboard/util/AssertUtils.java 0 → 100644
  1 +package cn.iocoder.dashboard.util;
  2 +
  3 +import cn.hutool.core.util.ReflectUtil;
  4 +import cn.iocoder.dashboard.common.exception.ErrorCode;
  5 +import cn.iocoder.dashboard.common.exception.ServiceException;
  6 +import org.junit.jupiter.api.Assertions;
  7 +
  8 +import java.lang.reflect.Field;
  9 +import java.util.Arrays;
  10 +
  11 +/**
  12 + * 单元测试,assert 断言工具类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class AssertUtils {
  17 +
  18 + /**
  19 + * 比对两个对象的属性是否一致
  20 + *
  21 + * 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略
  22 + *
  23 + * @param expected 期望对象
  24 + * @param actual 实际对象
  25 + */
  26 + public static void assertEquals(Object expected, Object actual) {
  27 + Field[] expectedFields = ReflectUtil.getFields(expected.getClass());
  28 + Arrays.stream(expectedFields).forEach(expectedField -> {
  29 + // 忽略不存在的属性
  30 + Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName());
  31 + if (actualField == null) {
  32 + return;
  33 + }
  34 + // 比对
  35 + Assertions.assertEquals(
  36 + ReflectUtil.getFieldValue(expected, expectedField),
  37 + ReflectUtil.getFieldValue(actual, actualField),
  38 + String.format("Field(%s) 不匹配", expectedField.getName())
  39 + );
  40 + });
  41 + }
  42 +
  43 + /**
  44 + * 比对抛出的 ServiceException 是否匹配
  45 + *
  46 + * @param errorCode 错误码对象
  47 + * @param serviceException 业务异常
  48 + */
  49 + public static void assertEquals(ErrorCode errorCode, ServiceException serviceException) {
  50 + Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配");
  51 + Assertions.assertEquals(errorCode.getMessage(), serviceException.getMessage(), "错误提示不匹配");
  52 + }
  53 +
  54 +}
... ...
src/test/resources/application-unit-test.yaml 0 → 100644
  1 +spring:
  2 + main:
  3 + lazy-initialization: true
  4 +
  5 + # 去除的自动配置项
  6 + autoconfigure:
  7 + exclude:
  8 + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration # 单元测试,禁用 SpringSecurity
  9 + - org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration # 单元测试,禁用 SpringSecurity
  10 + - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 单元测试,禁用 Quartz
  11 + - com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration # 单元测试,禁用 Lock4j 分布式锁
  12 +
  13 +--- #################### 数据库相关配置 ####################
  14 +
  15 +spring:
  16 + # 数据源配置项
  17 + datasource:
  18 + name: ruoyi-vue-pro
  19 + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
  20 + driver-class-name: org.h2.Driver
  21 + username: sa
  22 + password:
  23 + schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
  24 +
  25 + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
  26 + redis:
  27 + host: 127.0.0.1 # 地址
  28 + port: 6379 # 端口
  29 + database: 0 # 数据库索引
  30 +
  31 +--- #################### 定时任务相关配置 ####################
  32 +
  33 +# Quartz 配置项,对应 QuartzProperties 配置类(单元测试,禁用 Quartz)
  34 +
  35 +--- #################### 配置中心相关配置 ####################
  36 +
  37 +# Apollo 配置中心
  38 +apollo:
  39 + bootstrap:
  40 + enabled: false # 单元测试,禁用配置中心
  41 +
  42 +--- #################### 服务保障相关配置 ####################
  43 +
  44 +# Lock4j 配置项(单元测试,禁用 Lock4j)
  45 +
  46 +# Resilience4j 配置项
  47 +resilience4j:
  48 + ratelimiter:
  49 + instances:
  50 + backendA:
  51 + limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
  52 + limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
  53 + timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
  54 + register-health-indicator: true # 是否注册到健康监测
  55 +
  56 +--- #################### 监控相关配置 ####################
  57 +
  58 +# Actuator 监控端点的配置项
  59 +management:
  60 + endpoints:
  61 + enabled-by-default: false
  62 +
  63 +# Spring Boot Admin 配置项
  64 +spring:
  65 + boot:
  66 + admin:
  67 + # Spring Boot Admin Client 客户端的相关配置
  68 + client:
  69 + enabled: false
  70 + # Spring Boot Admin Server 服务端的相关配置
  71 + context-path: /admin # 配置 Spring
  72 +
  73 +# 日志文件配置
  74 +logging:
  75 + file:
  76 + path: ${user.home}/logs/ # 日志文件的路径
  77 +
  78 +--- #################### 芋道相关配置 ####################
  79 +
  80 +# 芋道配置项,设置当前项目所有自定义的配置
  81 +yudao:
  82 + info:
  83 + version: 1.0.0
  84 + base-package: cn.iocoder.dashboard
  85 + web:
  86 + api-prefix: /api
  87 + controller-package: ${yudao.info.base-package}
  88 + security:
  89 + token-header: Authorization
  90 + token-secret: abcdefghijklmnopqrstuvwxyz
  91 + token-timeout: 1d
  92 + session-timeout: 30m
  93 + mock-enable: true
  94 + mock-secret: test
  95 + swagger:
  96 + enable: false # 单元测试,禁用 Swagger
  97 + captcha:
  98 + timeout: 5m
  99 + width: 160
  100 + height: 60
  101 + file:
  102 + base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/
  103 + codegen:
  104 + base-package: ${yudao.info.base-package}.modules
  105 + db-schemas: ${spring.datasource.name}
  106 + xss:
  107 + enable: false
  108 + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
  109 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
  110 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
... ...
src/test/resources/application.yaml deleted
1   -spring:
2   - application:
3   - name: dashboard
4   -
5   - profiles:
6   - active: local
7   -
8   - # Servlet 配置
9   - servlet:
10   - # 文件上传相关配置项
11   - multipart:
12   - max-file-size: 16MB # 单个文件大小
13   - max-request-size: 32MB # 设置总上传的文件大小
14   -
15   - # Jackson 配置项
16   - jackson:
17   - serialization:
18   - write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
19   - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
20   - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
21   - fail-on-empty-beans: false # 允许序列化无属性的 Bean
22   -
23   - main:
24   - lazy-initialization: true
25   -
26   - # 去除的自动配置项
27   - autoconfigure:
28   - exclude:
29   - - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
30   - - org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration
31   - - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
32   -
33   -# MyBatis Plus 的配置项
34   -mybatis-plus:
35   - configuration:
36   - map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
37   -# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
38   - global-config:
39   - db-config:
40   - id-type: AUTO # 自增 ID
41   - logic-delete-value: 1 # 逻辑已删除值(默认为 1)
42   - logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
43   - mapper-locations: classpath*:mapper/*.xml
44   - type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject
src/test/resources/sql/clean.sql 0 → 100644
  1 +-- inf 开头的 DB
  2 +DELETE FROM "inf_config";
  3 +
  4 +-- sys 开头的 DB
  5 +DELETE FROM "sys_dept";
  6 +DELETE FROM "sys_dict_data";
  7 +DELETE FROM "sys_role";
  8 +DELETE FROM "sys_role_menu";
  9 +DELETE FROM "sys_menu";
... ...
src/test/resources/sql/create_tables.sql 0 → 100644
  1 +-- inf 开头的 DB
  2 +
  3 +CREATE TABLE "inf_config" (
  4 + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  5 + "group" varchar(50) NOT NULL,
  6 + "type" tinyint NOT NULL,
  7 + "name" varchar(100) NOT NULL DEFAULT '',
  8 + "key" varchar(100) NOT NULL DEFAULT '',
  9 + "value" varchar(500) NOT NULL DEFAULT '',
  10 + "sensitive" bit NOT NULL,
  11 + "remark" varchar(500) DEFAULT NULL,
  12 + "create_by" varchar(64) DEFAULT '',
  13 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  14 + "update_by" varchar(64) DEFAULT '',
  15 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  16 + "deleted" bit NOT NULL DEFAULT FALSE,
  17 + PRIMARY KEY ("id")
  18 +) COMMENT '参数配置表';
  19 +
  20 +-- sys 开头的 DB
  21 +
  22 +CREATE TABLE "sys_dept" (
  23 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  24 + "name" varchar(30) NOT NULL DEFAULT '',
  25 + "parent_id" bigint NOT NULL DEFAULT '0',
  26 + "sort" int NOT NULL DEFAULT '0',
  27 + "leader" varchar(20) DEFAULT NULL,
  28 + "phone" varchar(11) DEFAULT NULL,
  29 + "email" varchar(50) DEFAULT NULL,
  30 + "status" tinyint NOT NULL,
  31 + "create_by" varchar(64) DEFAULT '',
  32 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  33 + "update_by" varchar(64) DEFAULT '',
  34 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  35 + "deleted" bit NOT NULL DEFAULT FALSE,
  36 + PRIMARY KEY ("id")
  37 +) COMMENT '部门表';
  38 +
  39 +CREATE TABLE "sys_dict_data" (
  40 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  41 + "sort" int NOT NULL DEFAULT '0',
  42 + "label" varchar(100) NOT NULL DEFAULT '',
  43 + "value" varchar(100) NOT NULL DEFAULT '',
  44 + "dict_type" varchar(100) NOT NULL DEFAULT '',
  45 + "status" tinyint NOT NULL DEFAULT '0',
  46 + "remark" varchar(500) DEFAULT NULL,
  47 + "create_by" varchar(64) DEFAULT '',
  48 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  49 + "update_by" varchar(64) DEFAULT '',
  50 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  51 + "deleted" bit NOT NULL DEFAULT FALSE,
  52 + PRIMARY KEY ("id")
  53 +) COMMENT '字典数据表';
  54 +
  55 +CREATE TABLE "sys_role" (
  56 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  57 + "name" varchar(30) NOT NULL,
  58 + "code" varchar(100) NOT NULL,
  59 + "sort" int NOT NULL,
  60 + "data_scope" tinyint NOT NULL DEFAULT '1',
  61 + "data_scope_dept_ids" varchar(500) NOT NULL DEFAULT '',
  62 + "status" tinyint NOT NULL,
  63 + "type" tinyint NOT NULL,
  64 + "remark" varchar(500) DEFAULT NULL,
  65 + "create_by" varchar(64) DEFAULT '',
  66 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  67 + "update_by" varchar(64) DEFAULT '',
  68 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  69 + "deleted" bit NOT NULL DEFAULT FALSE,
  70 + PRIMARY KEY ("id")
  71 +) COMMENT '角色信息表';
  72 +
  73 +CREATE TABLE "sys_role_menu" (
  74 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  75 + "role_id" bigint NOT NULL,
  76 + "menu_id" bigint NOT NULL,
  77 + "create_by" varchar(64) DEFAULT '',
  78 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  79 + "update_by" varchar(64) DEFAULT '',
  80 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  81 + "deleted" bit NOT NULL DEFAULT FALSE,
  82 + PRIMARY KEY ("id")
  83 +) COMMENT '角色和菜单关联表';
  84 +
  85 +CREATE TABLE "sys_menu" (
  86 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  87 + "name" varchar(50) NOT NULL,
  88 + "permission" varchar(100) NOT NULL DEFAULT '',
  89 + "menu_type" tinyint NOT NULL,
  90 + "sort" int NOT NULL DEFAULT '0',
  91 + "parent_id" bigint NOT NULL DEFAULT '0',
  92 + "path" varchar(200) DEFAULT '',
  93 + "icon" varchar(100) DEFAULT '#',
  94 + "component" varchar(255) DEFAULT NULL,
  95 + "status" tinyint NOT NULL DEFAULT '0',
  96 + "create_by" varchar(64) DEFAULT '',
  97 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  98 + "update_by" varchar(64) DEFAULT '',
  99 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  100 + "deleted" bit NOT NULL DEFAULT FALSE,
  101 + PRIMARY KEY ("id")
  102 +) COMMENT '菜单权限表';
... ...