Skip to main content
 首页 » 编程设计

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

2025年02月15日22luoye11

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

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

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

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

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

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

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

如何处理它的其他想法:
  • MySQL 和 H2 的单独演进(不是一个好主意)
  • 使 H2 忽略 create table 中的其他 MySQL 内容的某种方法(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 ,是否可以在没有太多痛苦和怪癖的情况下集成它?

    请您参考如下方法:

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

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

    使用 DdlGenerator可以这样做:

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

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

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

    一些可能有用的链接:
  • http://blog.matthieuguillermin.fr/2012/03/unit-testing-tricks-for-play-2-0-and-ebean/
  • https://gist.github.com/nboire/2819920