14 changed files with 576 additions and 48 deletions
@ -0,0 +1,60 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<parent> |
||||
|
<groupId>cn.iocoder.boot</groupId> |
||||
|
<artifactId>yudao-framework</artifactId> |
||||
|
<version>${revision}</version> |
||||
|
</parent> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId> |
||||
|
<packaging>jar</packaging> |
||||
|
|
||||
|
<name>${project.artifactId}</name> |
||||
|
<description>测试组件,用于单元测试、集成测试</description> |
||||
|
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
||||
|
|
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>cn.iocoder.boot</groupId> |
||||
|
<artifactId>yudao-common</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- DB 相关 --> |
||||
|
<dependency> |
||||
|
<groupId>cn.iocoder.boot</groupId> |
||||
|
<artifactId>yudao-spring-boot-starter-mybatis</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>cn.iocoder.boot</groupId> |
||||
|
<artifactId>yudao-spring-boot-starter-redis</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- Test 测试相关 --> |
||||
|
<dependency> |
||||
|
<groupId>org.mockito</groupId> |
||||
|
<artifactId>mockito-inline</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>com.h2database</groupId> <!-- 单元测试,我们采用 H2 作为数据库 --> |
||||
|
<artifactId>h2</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> |
||||
|
<artifactId>jedis-mock</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> |
||||
|
<artifactId>podam</artifactId> |
||||
|
</dependency> |
||||
|
</dependencies> |
||||
|
</project> |
||||
@ -0,0 +1,35 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.config; |
||||
|
|
||||
|
import com.github.fppt.jedismock.RedisServer; |
||||
|
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; |
||||
|
|
||||
|
/** |
||||
|
* Redis 测试 Configuration,主要实现内嵌 Redis 的启动 |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
@Configuration(proxyBeanMethods = false) |
||||
|
@Lazy(false) // 禁止延迟加载
|
||||
|
@EnableConfigurationProperties(RedisProperties.class) |
||||
|
public class RedisTestConfiguration { |
||||
|
|
||||
|
/** |
||||
|
* 创建模拟的 Redis Server 服务器 |
||||
|
*/ |
||||
|
@Bean |
||||
|
public RedisServer redisServer(RedisProperties properties) throws IOException { |
||||
|
RedisServer redisServer = new RedisServer(properties.getPort()); |
||||
|
// 一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。
|
||||
|
try { |
||||
|
redisServer.start(); |
||||
|
} catch (Exception ignore) {} |
||||
|
return redisServer; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.config; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; |
||||
|
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties; |
||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
||||
|
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; |
||||
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer; |
||||
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.context.annotation.Lazy; |
||||
|
|
||||
|
import javax.sql.DataSource; |
||||
|
|
||||
|
/** |
||||
|
* SQL 初始化的测试 Configuration |
||||
|
* |
||||
|
* 为什么不使用 org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration 呢? |
||||
|
* 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true,开启延迟加载。此时,会导致 DataSourceInitializationConfiguration 初始化 |
||||
|
* 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈! |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
@Configuration(proxyBeanMethods = false) |
||||
|
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class) |
||||
|
@ConditionalOnSingleCandidate(DataSource.class) |
||||
|
@ConditionalOnClass(name = "org.springframework.jdbc.datasource.init.DatabasePopulator") |
||||
|
@Lazy(value = false) // 禁止延迟加载
|
||||
|
@EnableConfigurationProperties(SqlInitializationProperties.class) |
||||
|
public class SqlInitializationTestConfiguration { |
||||
|
|
||||
|
@Bean |
||||
|
public DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource, |
||||
|
SqlInitializationProperties initializationProperties) { |
||||
|
DatabaseInitializationSettings settings = createFrom(initializationProperties); |
||||
|
return new DataSourceScriptDatabaseInitializer(dataSource, settings); |
||||
|
} |
||||
|
|
||||
|
static DatabaseInitializationSettings createFrom(SqlInitializationProperties properties) { |
||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); |
||||
|
settings.setSchemaLocations(properties.getSchemaLocations()); |
||||
|
settings.setDataLocations(properties.getDataLocations()); |
||||
|
settings.setContinueOnError(properties.isContinueOnError()); |
||||
|
settings.setSeparator(properties.getSeparator()); |
||||
|
settings.setEncoding(properties.getEncoding()); |
||||
|
settings.setMode(properties.getMode()); |
||||
|
return settings; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.ut; |
||||
|
|
||||
|
import cn.hutool.extra.spring.SpringUtil; |
||||
|
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; |
||||
|
import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; |
||||
|
import com.alibaba.druid.spring.boot3.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.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; |
||||
|
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 + Redis 的单元测试 |
||||
|
* |
||||
|
* 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis |
||||
|
* |
||||
|
* @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 配置类
|
||||
|
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
||||
|
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
||||
|
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
||||
|
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
|
||||
|
SqlInitializationTestConfiguration.class, // SQL 初始化
|
||||
|
// MyBatis 配置类
|
||||
|
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||
|
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||
|
|
||||
|
// Redis 配置类
|
||||
|
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
|
||||
|
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||
|
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
|
RedissonAutoConfiguration.class, // Redisson 自动配置类
|
||||
|
|
||||
|
// 其它配置类
|
||||
|
SpringUtil.class |
||||
|
}) |
||||
|
public static class Application { |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.ut; |
||||
|
|
||||
|
import cn.hutool.extra.spring.SpringUtil; |
||||
|
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; |
||||
|
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure; |
||||
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; |
||||
|
import com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration; |
||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; |
||||
|
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 配置类
|
||||
|
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
||||
|
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
||||
|
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
||||
|
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
|
||||
|
SqlInitializationTestConfiguration.class, // SQL 初始化
|
||||
|
// MyBatis 配置类
|
||||
|
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||
|
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||
|
MybatisPlusJoinAutoConfiguration.class, // MyBatis 的Join配置类
|
||||
|
|
||||
|
// 其它配置类
|
||||
|
SpringUtil.class |
||||
|
}) |
||||
|
public static class Application { |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.ut; |
||||
|
|
||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
|
||||
|
/** |
||||
|
* 纯 Mockito 的单元测试 |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
@ExtendWith(MockitoExtension.class) |
||||
|
public class BaseMockitoUnitTest { |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.ut; |
||||
|
|
||||
|
import cn.hutool.extra.spring.SpringUtil; |
||||
|
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
||||
|
import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; |
||||
|
import org.redisson.spring.starter.RedissonAutoConfiguration; |
||||
|
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
import org.springframework.context.annotation.Import; |
||||
|
import org.springframework.test.context.ActiveProfiles; |
||||
|
|
||||
|
/** |
||||
|
* 依赖内存 Redis 的单元测试 |
||||
|
* |
||||
|
* 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class) |
||||
|
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
|
||||
|
public class BaseRedisUnitTest { |
||||
|
|
||||
|
@Import({ |
||||
|
// Redis 配置类
|
||||
|
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
|
||||
|
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
|
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||
|
RedissonAutoConfiguration.class, // Redisson 自动配置类
|
||||
|
|
||||
|
// 其它配置类
|
||||
|
SpringUtil.class |
||||
|
}) |
||||
|
public static class Application { |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
/** |
||||
|
* 提供单元测试 Unit Test 的基类 |
||||
|
*/ |
||||
|
package cn.iocoder.yudao.framework.test.core.ut; |
||||
@ -0,0 +1,101 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.util; |
||||
|
|
||||
|
import cn.hutool.core.util.ArrayUtil; |
||||
|
import cn.hutool.core.util.ReflectUtil; |
||||
|
import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
||||
|
import cn.iocoder.yudao.framework.common.exception.ServiceException; |
||||
|
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
||||
|
import org.junit.jupiter.api.Assertions; |
||||
|
import org.junit.jupiter.api.function.Executable; |
||||
|
|
||||
|
import java.lang.reflect.Field; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
import static org.junit.jupiter.api.Assertions.assertThrows; |
||||
|
|
||||
|
/** |
||||
|
* 单元测试,assert 断言工具类 |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
public class AssertUtils { |
||||
|
|
||||
|
/** |
||||
|
* 比对两个对象的属性是否一致 |
||||
|
* |
||||
|
* 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 |
||||
|
* |
||||
|
* @param expected 期望对象 |
||||
|
* @param actual 实际对象 |
||||
|
* @param ignoreFields 忽略的属性数组 |
||||
|
*/ |
||||
|
public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) { |
||||
|
Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); |
||||
|
Arrays.stream(expectedFields).forEach(expectedField -> { |
||||
|
// 忽略 jacoco 自动生成的 $jacocoData 属性的情况
|
||||
|
if (expectedField.isSynthetic()) { |
||||
|
return; |
||||
|
} |
||||
|
// 如果是忽略的属性,则不进行比对
|
||||
|
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { |
||||
|
return; |
||||
|
} |
||||
|
// 忽略不存在的属性
|
||||
|
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); |
||||
|
if (actualField == null) { |
||||
|
return; |
||||
|
} |
||||
|
// 比对
|
||||
|
Assertions.assertEquals( |
||||
|
ReflectUtil.getFieldValue(expected, expectedField), |
||||
|
ReflectUtil.getFieldValue(actual, actualField), |
||||
|
String.format("Field(%s) 不匹配", expectedField.getName()) |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 比对两个对象的属性是否一致 |
||||
|
* |
||||
|
* 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 |
||||
|
* |
||||
|
* @param expected 期望对象 |
||||
|
* @param actual 实际对象 |
||||
|
* @param ignoreFields 忽略的属性数组 |
||||
|
* @return 是否一致 |
||||
|
*/ |
||||
|
public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) { |
||||
|
Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); |
||||
|
return Arrays.stream(expectedFields).allMatch(expectedField -> { |
||||
|
// 如果是忽略的属性,则不进行比对
|
||||
|
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { |
||||
|
return true; |
||||
|
} |
||||
|
// 忽略不存在的属性
|
||||
|
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); |
||||
|
if (actualField == null) { |
||||
|
return true; |
||||
|
} |
||||
|
return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField), |
||||
|
ReflectUtil.getFieldValue(actual, actualField)); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行方法,校验抛出的 Service 是否符合条件 |
||||
|
* |
||||
|
* @param executable 业务异常 |
||||
|
* @param errorCode 错误码对象 |
||||
|
* @param messageParams 消息参数 |
||||
|
*/ |
||||
|
public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) { |
||||
|
// 调用方法
|
||||
|
ServiceException serviceException = assertThrows(ServiceException.class, executable); |
||||
|
// 校验错误码
|
||||
|
Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); |
||||
|
String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams); |
||||
|
Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
package cn.iocoder.yudao.framework.test.core.util; |
||||
|
|
||||
|
import cn.hutool.core.date.LocalDateTimeUtil; |
||||
|
import cn.hutool.core.util.ArrayUtil; |
||||
|
import cn.hutool.core.util.RandomUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
||||
|
import uk.co.jemos.podam.api.PodamFactory; |
||||
|
import uk.co.jemos.podam.api.PodamFactoryImpl; |
||||
|
|
||||
|
import java.lang.reflect.Type; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.function.Consumer; |
||||
|
import java.util.stream.Collectors; |
||||
|
import java.util.stream.Stream; |
||||
|
|
||||
|
/** |
||||
|
* 随机工具类 |
||||
|
* |
||||
|
* @author 芋道源码 |
||||
|
*/ |
||||
|
public class RandomUtils { |
||||
|
|
||||
|
private static final int RANDOM_STRING_LENGTH = 10; |
||||
|
|
||||
|
private static final int TINYINT_MAX = 127; |
||||
|
|
||||
|
private static final int RANDOM_DATE_MAX = 30; |
||||
|
|
||||
|
private static final int RANDOM_COLLECTION_LENGTH = 5; |
||||
|
|
||||
|
private static final PodamFactory PODAM_FACTORY = new PodamFactoryImpl(); |
||||
|
|
||||
|
static { |
||||
|
// 字符串
|
||||
|
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class, |
||||
|
(dataProviderStrategy, attributeMetadata, map) -> randomString()); |
||||
|
// Integer
|
||||
|
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Integer.class, (dataProviderStrategy, attributeMetadata, map) -> { |
||||
|
// 如果是 status 的字段,返回 0 或 1
|
||||
|
if ("status".equals(attributeMetadata.getAttributeName())) { |
||||
|
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); |
||||
|
} |
||||
|
// 如果是 type、status 结尾的字段,返回 tinyint 范围
|
||||
|
if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), |
||||
|
"type", "status", "category", "scope", "result")) { |
||||
|
return RandomUtil.randomInt(0, TINYINT_MAX + 1); |
||||
|
} |
||||
|
return RandomUtil.randomInt(); |
||||
|
}); |
||||
|
// LocalDateTime
|
||||
|
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class, |
||||
|
(dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime()); |
||||
|
// Boolean
|
||||
|
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> { |
||||
|
// 如果是 deleted 的字段,返回非删除
|
||||
|
if ("deleted".equals(attributeMetadata.getAttributeName())) { |
||||
|
return false; |
||||
|
} |
||||
|
return RandomUtil.randomBoolean(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static String randomString() { |
||||
|
return RandomUtil.randomString(RANDOM_STRING_LENGTH); |
||||
|
} |
||||
|
|
||||
|
public static Long randomLongId() { |
||||
|
return RandomUtil.randomLong(0, Long.MAX_VALUE); |
||||
|
} |
||||
|
|
||||
|
public static Integer randomInteger() { |
||||
|
return RandomUtil.randomInt(0, Integer.MAX_VALUE); |
||||
|
} |
||||
|
|
||||
|
public static Date randomDate() { |
||||
|
return RandomUtil.randomDay(0, RANDOM_DATE_MAX); |
||||
|
} |
||||
|
|
||||
|
public static LocalDateTime randomLocalDateTime() { |
||||
|
// 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳
|
||||
|
return LocalDateTimeUtil.of(randomDate()).withNano(0); |
||||
|
} |
||||
|
|
||||
|
public static Short randomShort() { |
||||
|
return (short) RandomUtil.randomInt(0, Short.MAX_VALUE); |
||||
|
} |
||||
|
|
||||
|
public static <T> Set<T> randomSet(Class<T> clazz) { |
||||
|
return Stream.iterate(0, i -> i).limit(RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH)) |
||||
|
.map(i -> randomPojo(clazz)).collect(Collectors.toSet()); |
||||
|
} |
||||
|
|
||||
|
public static Integer randomCommonStatus() { |
||||
|
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); |
||||
|
} |
||||
|
|
||||
|
public static String randomEmail() { |
||||
|
return randomString() + "@qq.com"; |
||||
|
} |
||||
|
|
||||
|
public static String randomMobile() { |
||||
|
return "13800138" + RandomUtil.randomNumbers(3); |
||||
|
} |
||||
|
|
||||
|
public static String randomURL() { |
||||
|
return "https://www.iocoder.cn/" + randomString(); |
||||
|
} |
||||
|
|
||||
|
@SafeVarargs |
||||
|
public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) { |
||||
|
T pojo = PODAM_FACTORY.manufacturePojo(clazz); |
||||
|
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
|
||||
|
if (ArrayUtil.isNotEmpty(consumers)) { |
||||
|
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); |
||||
|
} |
||||
|
return pojo; |
||||
|
} |
||||
|
|
||||
|
@SafeVarargs |
||||
|
public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) { |
||||
|
T pojo = PODAM_FACTORY.manufacturePojo(clazz, type); |
||||
|
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
|
||||
|
if (ArrayUtil.isNotEmpty(consumers)) { |
||||
|
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); |
||||
|
} |
||||
|
return pojo; |
||||
|
} |
||||
|
|
||||
|
@SafeVarargs |
||||
|
public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) { |
||||
|
int size = RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH); |
||||
|
return randomPojoList(clazz, size, consumers); |
||||
|
} |
||||
|
|
||||
|
@SafeVarargs |
||||
|
public static <T> List<T> randomPojoList(Class<T> clazz, int size, Consumer<T>... consumers) { |
||||
|
return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)) |
||||
|
.collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
/** |
||||
|
* 测试组件,用于单元测试、集成测试等等 |
||||
|
*/ |
||||
|
package cn.iocoder.yudao.framework.test; |
||||
@ -0,0 +1 @@ |
|||||
|
<https://www.iocoder.cn/Spring-Boot/Unit-Test/?yudao> |
||||
Loading…
Reference in new issue