Unit testing 如何针对非内存数据库(如MySQL in Play framework)创建单元测试,并重置为已知状态?
我想创建单元测试,涵盖在Play framework 2.1.0中使用关系数据库的代码。这有很多可能,并且都会导致问题: 内存H2数据库的测试 Play framework文档建议在H2内存数据库上运行单元测试,即使用于开发和生产的主数据库使用其他软件(如MySQL):Unit testing 如何针对非内存数据库(如MySQL in Play framework)创建单元测试,并重置为已知状态?,unit-testing,playframework,playframework-2.0,database-testing,Unit Testing,Playframework,Playframework 2.0,Database Testing,我想创建单元测试,涵盖在Play framework 2.1.0中使用关系数据库的代码。这有很多可能,并且都会导致问题: 内存H2数据库的测试 Play framework文档建议在H2内存数据库上运行单元测试,即使用于开发和生产的主数据库使用其他软件(如MySQL): app=Helpers.fakeApplication(Helpers.inMemoryDatabase()); 我的应用程序不使用复杂的RDBMS特性,例如存储过程,并且大多数数据库访问案例都是ebean调用,因此它应该与M
app=Helpers.fakeApplication(Helpers.inMemoryDatabase());
我的应用程序不使用复杂的RDBMS特性,例如存储过程,并且大多数数据库访问案例都是ebean调用,因此它应该与MySQL和H2兼容
然而,evolutions中的表创建语句使用MySQL特有的功能,例如指定ENGINE=InnoDB
,DEFAULT CHARACTER SET=utf8
,等等。我担心如果我删除CREATE table
的这些专有部分,MySQL将使用一些我无法控制且取决于版本的默认设置,所以,为了测试和开发应用程序,必须修改主MySQL配置
有人使用过这种方法(使evolutions与MySQL和H2兼容)
如何处理它的其他想法:
- MySQL和H2的独立演进(不是一个好主意)
- 在
(MySQL兼容模式不起作用,即使在create table
默认字符集上,它仍然会抱怨)中,H2会忽略额外的MySQL内容。我不知道怎么做
Map settings=newhashmap();
settings.put(“db.default.url”,“jdbc:mysql://localhost/sometestdatabase");
settings.put(“db.default.jndiName”、“DefaultDS”);
app=Helpers.fakeApplication(设置);
看起来进化在这里起作用,但在每次测试之前如何最好地清理数据库呢?通过创建截断每个表的自定义代码?如果它将删除表,那么在下一次测试之前,演进是否会再次运行,或者每个play test
命令应用它们一次?或者每次调用Helpers.fakeApplication()
一次
这里的最佳实践是什么?听说过,有没有可能不费吹灰之力就集成它?首先,我建议您在测试和生产中使用相同的RDBMS,因为它可以避免一些难以发现的bug 关于在每次测试之间清理数据库的需要,您可以使用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
创建,避免一些样板代码
一些可能有用的链接:
公共类测试{
// ...
@以前
public void startApp()引发异常{
//设置与测试数据库的连接,与主数据库不同。应使用更好的配置,而不是硬编码。
映射设置=新建HashMap();
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”);//通过JNDI使连接对dbunit可用
app=Helpers.fakeApplication(设置);
Helpers.start(应用程序);
databaseTester=新的JndiDatabaseTester(“DefaultDS”);
IDataSet initialDataSet=new FlatXmlDataSetBuilder().build(play.play.application())
.resourceAsStream(“/resources/dataset.xml”);
databaseTester.setDataSet(initialDataSet);
databaseTester.onSetup();
}
@之后
public void stopApp()引发异常{
databaseTester.onTearDown();
Helpers.stop(应用程序);
}
}
Mydataset.xml
只包含表名,用于在每次测试之前告诉dbunit清空这些表。它还可以包含装置
当使用这种方法时,演进会在测试数据库上自动运行,所以若您从测试数据库中删除所有表,它们将被重新创建
如果您只需要清理表,那么使用dbunit就太过分了。您可以通过直接发出查询或使用ebeanDdlGenerator
来清理表。但我也使用dbunit来比较数据
我不使用Helpers.running
,因为它需要Runnable
并且Runnable
实现不能抛出异常-这对测试非常不方便。但是如果您查看running()
的代码,它只调用Helpers.start()
和Helpers.stop()
,所以我直接在@Before
和@Before
中调用这些方法
决定不使用H2来运行测试:是的,它运行得更快,但它和MySQL之间有太多的区别
有人使用过这种方法(使evolutions与MySQL和H2兼容)
我已经找到了MySQL特定功能的答案:当我为我的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
}
});
}