有没有办法为 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,然后在创建的实例上调用 setDriverClassNamesetUrl

然后我们在数据源上使用 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 脚本,或者使用 &lt;jdbc:initialize-database...&gt; 标签运行一些 MySQL 脚本。

【讨论】:

很遗憾,H2 不支持很多语句,例如 UNIQUE KEYCREATE EVENTINSERT 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

测试用例在完成 beforeAll 之前执行。?

有没有办法使用CLI(命令行界面)使用junit测试用例测试我的项目的jar文件?

使用 UI 测试用例在 android studio 中运行测试

JAVA 批量执行测试用例

Jmeter常用脚本开发之Junit Request