如何在Play框架中针对非内存数据库(如MySQL)创建单元测试,并重置为已知状态?

kol*_*len 23 unit-testing database-testing playframework playframework-2.0

我想创建单元测试,涵盖在Play框架2.1.0中使用关系数据库的代码.这有很多可能性,所有原因都有问题:

测试内存H2数据库

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

app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
Run Code Online (Sandbox Code Playgroud)

我的应用程序不使用复杂的RDBMS功能,如存储过程,大多数数据库访问情况是ebean调用,因此它应该兼容MySQL和H2.

然而,在变阵创建表的语句中使用MySQL特定的功能,如指定ENGINE = InnoDB,DEFAULT CHARACTER SET = utf8等我担心我是否会删除这些专有部分CREATE TABLE,MySQL将使用一些默认设置,我无法控制和依赖的版本,所以测试和开发应用程序主MySQL配置必须修改.

任何人都使用这种方法(使evolutions与MySQL和H2兼容)?

其他想法如何处理:

  • MySQL和H2分开演变(不是个好主意)
  • 有些方法让H2忽略了额外的MySQL内容create table(MySQL兼容模式不起作用,它仍然会抱怨default character set).我不知道怎么回事.

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

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

如何在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);
Run Code Online (Sandbox Code Playgroud)

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

这里的最佳做法是什么?听说dbunit,有没有可能整合它没有太多的痛苦和怪癖?

mgu*_*min 8

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

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

使用DdlGenerator可以这样做:

    EbeanServer server = Ebean.getServer(serverName);
    ServerConfig config = new ServerConfig();
    DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);
Run Code Online (Sandbox Code Playgroud)

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

它还允许您轻松自动化FakeApplication创建,避免一些样板代码.

一些可能有用的链接:


kol*_*len 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <name_of_my_first_table />
  <name_of_my_second_table />
</dataset>
Run Code Online (Sandbox Code Playgroud)

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

如果你只需要清理表就可以使用dbunit,你可以通过直接发出查询或使用ebean来清理它们DdlGenerator.但我也使用dbunit来比较数据.

我不使用Helpers.running,因为它需要RunnableRunnable实现不能抛出异常 - 非常不方便测试.但是如果你看代码running(),它只是调用Helpers.start(),Helpers.stop()所以我直接在@Before和调用这些方法@After.

决定不使用H2进行运行测试:是的,运行速度更快,但它与MySQL之间存在太大差异.