文章目录
pom 文件导入 jar 包依赖
<!-- 模拟数据库时使用 -->
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>test</scope>
</dependency>
<!-- 需要启动 redis 服务时使用 -->
<!-- https://mvnrepository.com/artifact/ai.grakn/redis-mock -->
<dependency>
<groupId>ai.grakn</groupId>
<artifactId>redis-mock</artifactId>
<version>0.1.6</version>
<scope>test</scope>
</dependency>
<!-- 需要使用 zookeeper server 时使用 -->
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-test -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<version>5.0.0</version>
<scope>test</scope>
</dependency>
编写 H2 数据库工具类
工具类主要用来执行 sql 语句文件,初始化脚本请在 配置文件中配置 schema
import lombok.extern.slf4j.Slf4j;
import org.h2.tools.RunScript;
import java.io.FileReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* insert description here
*
* @author Showa.L
* @since 2020/6/17 17:16
*/
@Slf4j
public class H2Utils {
private static Connection CONNECTION = null;
static {
try {
// 加载驱动
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException ex) {
log.error("get driver error", ex);
}
}
private static void setupConn(String url) throws SQLException {
if (null == CONNECTION || CONNECTION.isClosed()) {
// 获取链接
CONNECTION = DriverManager.getConnection(url, "admin", "admin123");
}
}
private static Statement getStatement() throws SQLException {
if (null == CONNECTION || CONNECTION.isClosed()) {
SQLException ex = new SQLException("No valid database connection!");
throw ex;
}
return CONNECTION.createStatement();
}
/**
* 关闭工具类的使用
*/
public static void close() {
try {
CONNECTION.close();
} catch (SQLException throwables) {
log.error("run script error", throwables);
}
}
/**
* 开启工具类的使用
*/
public static void start(String db) {
try {
setupConn(db);
} catch (SQLException throwables) {
log.error("run script error", throwables);
}
}
/**
* 执行 sql 语句文件
*/
public static void runScript(String filename) {
String realPath = H2Utils.class.getResource(filename).getPath();
try (Reader reader = new FileReader(realPath)) {
RunScript.execute(CONNECTION, reader);
} catch (Exception e) {
log.error("run script error", e);
}
}
}
Spring Boot 容器模拟程序启动基类——ApplicationTests
一些需要启动整个环境的测试则需要继承该类
/**
* insert description here
*
* @author Showa.L
* @since 2020/6/23 16:33
*/
@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ApplicationTests {
// 测试完成后停止资源
// redis 客户端,未配置则不需要
private static LettuceConnectionFactory LETTUCE_C_C;
@Autowired(required = false)
private LettuceConnectionFactory lettuceConnectionFactory;
@Autowired
protected MockMvc mockMvc;
static {
ServersUtils.startRedisServer();
H2Utils.start("jdbc:h2:mem:db_tests;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE");
Runtime.getRuntime().addShutdownHook(new Thread("xx-shutdown-hook") {
@Override
public void run() {
//资源释放动作
destory();
}
});
}
@PostConstruct
private void init() {
LETTUCE_C_C = lettuceConnectionFactory;
}
static void destory() {
// H2Utils stop
H2Utils.close();
// 销毁 Redis 客户端,主要是重连机制引发的程序无法关闭的问题
if (LETTUCE_C_C != null)
LETTUCE_C_C.destroy();
ServersUtils.stopRedisServer();
}
}
模拟服务启动工具类——ServiceUtils
一些集成服务模拟:redis,zookeeper
import ai.grakn.redismock.RedisServer;
import lombok.extern.slf4j.Slf4j;
/**
* insert description here
*
* @author Showa.L
* @since 2020/6/18 9:02
*/
@Slf4j
public class ServersUtils {
private static RedisServer redisServer = null;
public static void startRedisServer() {
try {
if (redisServer == null) {
redisServer = RedisServer.newRedisServer();
redisServer.start();
log.info("start redis server: port={}", redisServer.getBindPort());
System.setProperty("spring.redis.port", Integer.toString(redisServer.getBindPort()));
}
} catch (Exception e) {
log.error("start redis server error", e);
stopRedisServer();
}
}
public static void stopRedisServer() {
if (redisServer != null)
redisServer.stop();
}
private static TestingServer zookeeperServer = null;
public static void startZookeeperServer(Integer port) {
try {
if (zookeeperServer == null) {
log.info("start zookeeper server: port={}", port);
zookeeperServer = new TestingServer(port);
}
} catch (Exception e) {
log.error("start zookeeper server error", e);
}
}
public static void stopZookeeperServer() {
try {
if (zookeeperServer != null)
zookeeperServer.close();
} catch (IOException e) {
log.error("stop zookeeper server error", e);
}
}
}
Controller 测试基类——BaseControllerTest
用来 mock 测试 api 接口
import com.google.gson.Gson;
import com.sdstc.OrderApplicationTests;
import com.sdstc.core.utils.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.Map;
/**
* insert description here
*
* @author Showa.L
* @since 2020/6/19 9:30
*/
public class BaseControllerTests extends ApplicationTests {
protected ResultActions get(String path, HttpHeaders httpHeaders, Map<String, String> params) throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path);
if (httpHeaders != null)
requestBuilder.headers(httpHeaders);
if (params != null)
params.forEach(requestBuilder::param);
// 模拟 track_code。针对 RequestInfoForwardInterceptor 拦截引发的问题
requestBuilder.header("track_code", StringUtils.randomIntString(12));
return mockMvc.perform(requestBuilder);
}
protected ResultActions post(String path, HttpHeaders httpHeaders,
Map<String, String> params,
Object body) throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path);
if (httpHeaders != null)
requestBuilder.headers(httpHeaders);
if (params != null)
params.forEach(requestBuilder::param);
if (body != null)
requestBuilder.content(new Gson().toJson(body));
requestBuilder.contentType(MediaType.APPLICATION_JSON);
// 模拟 track_code。针对 RequestInfoForwardInterceptor 拦截引发的问题
requestBuilder.header("track_code", StringUtils.randomIntString(12));
return mockMvc.perform(requestBuilder);
}
}
Service 测试基类——BaseServiceTests
测试 service 时只需要测试方法逻辑的准确性,不需要启动整个容器,直接使用 mockito 测试
/**
* insert description here
*
* @author Showa.L
* @since 2020/6/18 16:58
*/
public class BaseServiceTests {
@BeforeEach
void initMockito() {
MockitoAnnotations.initMocks(this);
}
}
Dao 测试基类——BaseMapperTests
测试 dao 需要启动 spring 容器,所以需要继承 ApplicationTests
可以在这个基类中进行一些必要的准备
public class BaseMapperTests extends ApplicationTests {
}