有没有办法为 JUnit 测试用例在内存中运行 MySQL?
Posted
技术标签:
【中文标题】有没有办法为 JUnit 测试用例在内存中运行 MySQL?【英文标题】:Is there a way to run MySQL in-memory for JUnit test cases? 【发布时间】:2011-10-07 17:51:39 【问题描述】:我只是想为访问 mysql 数据库的服务添加测试用例,我想重新创建整个架构(在某些情况下,也只需使用 MySQL 转储文件,其中包含每个测试用例所需的数据)。我环顾四周,发现一些人使用 SQLite / H2 和其他人来执行此操作,但我只是在徘徊,是否有任何方法可以在内存中运行 MySQL,所以我不需要担心 MySQL 特有的任何事情我可能会在我们的服务中使用方言。
【问题讨论】:
【参考方案1】:这是使用专有 SQL 扩展通常不是一个好主意的原因之一。
我要做的是尝试找出您使用非标准 SQL 的地方并重构您的代码以将这些部分移至专用服务。然后你可以在运行单元测试时模拟这些。
【讨论】:
在某些情况下仍然可能出现问题(例如,某些词在一个 DBMS 中是关键字,而在其他 DBMS 中则不是),使用不同的 DBMS 进行测试可能无法消除它们。跨度> @Nicolae 显然,是的。您需要对这些方法进行专门的集成测试 尝试使用自动递增的唯一 64 位密钥编写一个简单的表。很难找到比这更典型的表了。现在确保它适用于 PgSQL、MySQL、MsSQL 和 SQLite。祝你好运。【参考方案2】:您可以为 JUnit 测试使用不同的架构。如果您使用的是 Spring,它的 JUnit 扩展允许每个测试在只读事务中运行,因此测试后没有数据将持久保存在数据库中。如果您需要测试的初始数据,请将所需数据放在参与事务的@Before
标记方法中。
【讨论】:
【参考方案3】:你可以挂载一个ramdrive(使用ImDisk),在上面复制你的数据文件,并在my.cnf中更改适当的配置后启动Mysql服务 单元测试数据库通常很小(为了快速测试,您应该保持它们很小),它们通常可以放入内存驱动器中。
您也可以考虑在 Spring 测试中使用事务,而不是在每次测试时重建表。
我们将它用于我们的开发团队,它就像一个魅力,我们的速度提高了一个数量级。
【讨论】:
【参考方案4】:我们使用 MySQL 和 flyway 来处理迁移。
对于单元测试和简单的集成测试,我们使用带有 MODE=MySQL
参数的 H2 内存数据库。 Mode=MySQL
使 H2 DB 能够处理大部分 MySQL 方言。
我们在 Spring 配置中的测试数据源是这样设置的:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
</bean>
(如果您不了解 Spring - XML 转换为调用 new BasicDataSource
,然后在创建的实例上调用 setDriverClassName
和 setUrl
)
然后我们在数据源上使用 Flyway 来创建模式并像我们对常规 MySQL DB 一样读取:
<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">
<property name="dataSource" ref="dataSource" />
<property name="cleanOnValidationError" value="false" />
<property name="initOnMigrate" value="true" />
<property name="sqlMigrationSuffix" value=".ddl" />
</bean>
您也可以只在 jdbcTemplate 中使用 dataSource bean 并以这种方式运行一些 SQL 脚本,或者使用 <jdbc:initialize-database...>
标签运行一些 MySQL 脚本。
【讨论】:
很遗憾,H2 不支持很多语句,例如UNIQUE KEY
、CREATE EVENT
和 INSERT IGNORE
。
但不是Mysql,还是容易出现不兼容问题
H2 也不支持 IF 函数 :( 原因:org.h2.jdbc.JdbcSQLException: Function "IF" not found; SQL statement: select if(len
H2 似乎也不支持 UNHEX 函数。
H2 甚至不支持枚举【参考方案5】:
使用与 MySQL 完全兼容且可在 JUnit 测试用例中使用的内存数据库的最简单方法是 imho MariaDB4j。 您只需要一个 Gradle (/Maven) 依赖项 (http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22mariaDB4j%22) 和几行代码即可开始:
DB database = DB.newEmbeddedDB(3306);
database.start();
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "");
一个启动脚本可以通过
database.source("path/to/resource.sql");
有关 GitHub 自述文件的更多信息: https://github.com/vorburger/MariaDB4j
编辑: 我对此答案有一些提示:MariaDB4j 似乎在系统临时文件夹中添加了文件。因此它将以嵌入式方式工作,这意味着无需安装任何东西,您只需通过所需的构建工具使用依赖项即可。但这不是真正的仅内存解决方案,因此我们不能再谈论单元测试,因为单元测试不能依赖文件或数据库
【讨论】:
【参考方案6】:我建议使用基于 docker 的 mysql/postgres/DB 测试容器。
Pom.xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.15.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.15.1</version>
<scope>test</scope>
</dependency>
XyzIT.java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@Testcontainers
Application-test.yml
datasource:
initialization-mode: always
schema: classpath*:schema-anyDb.sql #initial sql script to createDB
url: jdbc:tc:postgresql:11.9:///
jpa:
hibernate.ddl-auto: none
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
default_schema: public
show-sql: true
【讨论】:
以上是关于有没有办法为 JUnit 测试用例在内存中运行 MySQL?的主要内容,如果未能解决你的问题,请参考以下文章
在JUNIT测试中没有创建Hibernate Transaction
有没有办法使用CLI(命令行界面)使用junit测试用例测试我的项目的jar文件?