测试的时候经常使用H2内存数据库@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)但是H2数据库不支持一些mysql的操作,往往需要修改一些sql才能跑通测试。
使用内嵌mysql就可以解决这个问题,用起来跟H2没区别(完全没感到启动速度有差别),但是完全支持mysql的所有功能,非常值得一试
除了spring默认的test依赖之外,需要的依赖是
<dependency>
<groupId>com.wix</groupId>
<artifactId>wix-embedded-mysql</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
因为使用内嵌数据需要执行数据库初始化sql,需要把sql打包进jar包中,添加一下pom设置:
<build>
<resources>
<resource>
<!--sql的位置最好不要放在resources文件夹内-->
<directory>src/main/sql</directory>
<includes>
<include>**/*.sql</include>
</includes>
</resource>
</resources>
</build>
内嵌mysql数据库需要自己配置,在test包下创建EmbeddedMysqlServerConfig.java
参考wix 官方案例的配置如下:
import com.wix.mysql.EmbeddedMysql;
import com.wix.mysql.config.MysqldConfig;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
import java.io.IOException;
import java.net.ServerSocket;
import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql;
import static com.wix.mysql.config.Charset.UTF8;
import static com.wix.mysql.config.MysqldConfig.aMysqldConfig;
import static com.wix.mysql.distribution.Version.v5_6_latest;
import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_SINGLETON;
@TestConfiguration
public class EmbeddedMysqlServerConfig {
private static final String DATABASE_USERNAME = "root_test";
private static final String DATABASE_PASSWORD = "root_test";
private static final String DATABASE_NAME = "test_db";
@Bean
@Scope(value = SCOPE_SINGLETON)
public MysqldConfig config() throws IOException {
return aMysqldConfig(v5_6_latest).withCharset(UTF8).withPort(getRandomFreePort())
.withUser(DATABASE_USERNAME, DATABASE_PASSWORD).build();
}
@Bean(name = "embeddedMysql", destroyMethod = "stop")
public EmbeddedMysql embeddedMysql(MysqldConfig config) {
return anEmbeddedMysql(config)
.addSchema(DATABASE_NAME)
.start();
}
@Bean
@Primary
@DependsOn("embeddedMysql") // Because mySQLServer must be present before jdbc/hibernate
public DataSource dataSource(MysqldConfig mysqldConfig) {
return DataSourceBuilder.create().username(mysqldConfig.getUsername()).password(mysqldConfig.getPassword())
.url("jdbc:mysql://localhost:" + mysqldConfig.getPort() + "/" + DATABASE_NAME+"?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true")
.driverClassName("com.mysql.cj.jdbc.Driver").build();
}
private int getRandomFreePort() throws IOException {
// obtain random port for Mysql
try (ServerSocket serverSocket = new ServerSocket(0)) {
return serverSocket.getLocalPort();
}
}
}
用法
在test类上加上注解 👇
@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true", classes = EmbeddedMysqlServerConfig.class)
-
properties = "spring.main.allow-bean-definition-overriding=true"——test开启允许覆盖bean,将生产数据库源覆盖为内嵌mysql,如果test专属配置很多,也可以放在外部文件然后用@TestPropertySource("classpath:TestConfig.yml")启用 -
classes = EmbeddedMysqlServerConfig.class启用test专用的configuration配置类,如果配置类是此测试类的静态内部类的话也可以在内部类加上@SpringBootConfiguration来替代
@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true", classes = EmbeddedMysqlServerConfig.class)
//Sql注解可以放置多个,用于在测试开始之前初始化数据库
@Sql(scripts = "classpath:数据库A测试数据.sql")
@Sql(scripts = "classpath:数据库B测试数据.sql")//maven默认会把资源拷贝进classpath根目录
class MySpringBootTest {
//....
}
测试的一些其他注意点:
- 方法上也可以用
@Sql,但是如果需要和类上的@Sql一起生效需要添加注解@SqlMergeMode(value = SqlMergeMode.MergeMode.MERGE):
@SqlMergeMode(value = SqlMergeMode.MergeMode.MERGE)
@Sql(statements = "update xxx set a = 1 where b = 3", executionPhase = BEFORE_TEST_METHOD)
@Sql(statements = "update xxx set a = 1 where b = 2", executionPhase = BEFORE_TEST_METHOD)
void test1() {
}
- 如果需要指定test方法的执行顺序,需要在类上添加注解
@TestMethodOrder(MethodOrderer.OrderAnnotation.class),方法上添加@org.junit.jupiter.api.Order(注意,这不是spring的@Order):
@Order(1)
void test1() {
}
@Order(2)
void test1() {
}
- 测试中每个测试方法都是在独立的实例中(每个测试方法都会new一个该测试类)运行的,不共享类变量,如果想所有方法在同一个类实例中运行需要加类注解
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
防火墙通知
使用内嵌mysql的时候,每次都会弹出联网警告,win10关闭联网警告的方法如下:
Windows安全中心——>设置——>管理通知——> 关闭防火墙和网络保护通知 如果找不到按钮,可以
win+R打开gpedit.msc
计算机配置——> 管理模板——> Windows 件——> Windows 安全中心 ——> 通知 ——> 隐藏所有通知 ——> 启用