文章目錄
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 {
}