23

我想创建单元测试,涵盖在 Play 框架 2.1.0 中使用关系数据库的代码。这有很多可能性,并且都会导致问题:

在内存 H2 数据库上进行测试

Play 框架文档建议在 H2 内存数据库上运行单元测试,即使用于开发和生产的主数据库使用其他软件(即 MySQL):

app = Helpers.fakeApplication(Helpers.inMemoryDatabase());

我的应用程序不使用存储过程等复杂的 RDBMS 功能,并且大多数数据库访问案例都是 ebean 调用,因此它应该与 MySQL 和 H2 兼容。

但是,evolutions 中的表创建语句使用 MySQL 特定的功能,例如指定ENGINE = InnoDBDEFAULT CHARACTER SET = utf8等。我担心如果我将删除这些专有部分CREATE TABLE,MySQL 将使用一些我无法控制且取决于版本的默认设置,所以要测试和开发应用程序,必须修改主 MySQL 配置。

有人使用过这种方法(使进化与 MySQL 和 H2 兼容)吗?

如何处理它的其他想法:

  • MySQL 和 H2 的单独演进(不是一个好主意)
  • 使 H2 忽略其他 MySQL 内容的某种方法create table(MySQL 兼容模式不起作用,它仍然会抱怨default character set)。我不知道怎么做。

在与主数据库相同的数据库驱动程序上进行测试

H2 内存数据库的唯一优势是速度快,并且在相同的数据库驱动程序上进行测试可能比开发/生产数据库更好,因为它更接近真实环境。

如何在 Play 框架中正确完成?

试过:

Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/sometestdatabase");
settings.put("db.default.jndiName", "DefaultDS");
app = Helpers.fakeApplication(settings);

看起来进化在这里起作用,但是在每次测试之前最好如何清理数据库?通过创建截断每个表的自定义代码?如果它会删除表,那么进化会在下一次测试之前再次运行,还是每个play test命令应用一次?还是每次Helpers.fakeApplication()调用一次?

这里有哪些最佳实践?听说过dbunit,是否可以在没有太多痛苦和怪癖的情况下集成它?

4

5 回答 5

8

首先,我建议您使用相同的 RDBMS 进行测试和生产,因为它可以避免一些难以发现的错误。

关于在每次测试之间清理数据库的需要,您可以使用 EbeanDdlGenerator生成脚本来创建一个干净的数据库,并使用 JUnit 的@Before注释在每次测试之前自动执行这些脚本。

DdlGenerator可以这样使用:

    EbeanServer server = Ebean.getServer(serverName);
    ServerConfig config = new ServerConfig();
    DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);

这段代码可以放在一个基类中,您可以继承您的测试(或在Runner您可以与@RunWith注释一起使用的自定义中)。

它还可以让您轻松地自动FakeApplication创建,避免一些样板代码。

一些可能有用的链接:

于 2013-02-24T20:46:23.420 回答
5

我使用与主数据库和dbunit相同的数据库引擎在每次测试之前进行清理。

public class SomeTest {
    // ...

    @Before
    public void startApp() throws Exception {
        // Set up connection to test database, different from main database. Config better should be used instead of hard-coding.
        Map<String, String> settings = new HashMap<String, String>();
        settings.put("db.default.url", "jdbc:mysql://localhost/somedatabase?characterEncoding=UTF-8&useOldAliasMetadataBehavior=true");
        settings.put("db.default.user", "root");
        settings.put("db.default.password", "root");
        settings.put("db.default.jndiName", "DefaultDS"); // make connection available to dbunit through JNDI
        app = Helpers.fakeApplication(settings);
        Helpers.start(app);

        databaseTester = new JndiDatabaseTester("DefaultDS");

        IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(play.Play.application()
                .resourceAsStream("/resources/dataset.xml"));
        databaseTester.setDataSet(initialDataSet);
        databaseTester.onSetup();
    }

    @After
    public void stopApp() throws Exception {
        databaseTester.onTearDown();
        Helpers.stop(app);
    }
}

dataset.xml只包含表名来告诉 dbunit 在每次测试之前清空这些表。它还可以包含固定装置。

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <name_of_my_first_table />
  <name_of_my_second_table />
</dataset>

使用这种方法时,Evolutions 会在测试数据库上自动运行,因此如果您从测试数据库中删除所有表,它们将被重新创建。

如果您只需要清理表,则使用 dbunit 是多余的,您可以通过直接发出查询或使用 ebean 来清理它们DdlGenerator。但我也使用 dbunit 来比较数据。

我不使用Helpers.running,因为它需要Runnable并且Runnable实现不能抛出异常 - 对测试非常不方便。但是,如果您查看 的代码running(),它只是调用Helpers.start(),所以我直接在andHelpers.stop()中调用这些方法。@Before@After

决定不使用 H2 来运行测试:是的,它运行得更快,但是它和 MySQL 的区别太大了。

于 2013-02-28T13:50:16.483 回答
1

有人使用过这种方法(使进化与 MySQL 和 H2 兼容)吗?

我找到了 MySQL 特定功能的答案:如何使用 Play 2.x 对 MySQL 数据库进行单元测试?

于 2013-02-28T22:40:59.677 回答
1

当我为我的 postgres 数据库编写测试时,我只是创建了一个 HashMap 来连接到数据库,然后我编写了测试查询以确保存在正确数量的记录等等......这是我的代码。

    @Test
public void testDataBase() {
    final HashMap<String,String> postgres = new HashMap<String, String>();
    postgres.put("db.default.driver","org.postgresql.Driver");
    postgres.put("db.default.url","jdbc:postgresql://localhost/myDataBase");
    postgres.put("db.default.user", "postgres");
    postgres.put("db.default.password", "password");

    running(fakeApplication(postgres), new Runnable() {

        @Override
        public void run() {

            //Insert Assertions Here
        }
    });
}
于 2014-03-27T16:18:09.243 回答
1

如果目标是验证您的 Slick|JPA|Anorm 映射和基于的函数,您也可以使用 DB 模拟。

当它适合时,它具有比测试数据库更符合单元测试的优势,并且更易于管理(不设置/清除任务,不同步测试以避免访问相同的测试表)。

你可以看看我的框架 Acolyte ( http://github.com/cchantep/acolyte ),它在 Anorm 本身的规范中使用 (例如https://github.com/playframework/playframework/blob/master/framework/ src/anorm/src/test/scala/anorm/SqlResultSpec.scala)。

于 2014-08-08T11:15:27.240 回答