我想创建单元测试,其中包含在Play框架2.1.0中使用关系数据库的代码。这有很多可能性,并且都会导致问题:
Play框架文档建议在H2内存数据库上运行单元测试,即使用于开发和生产的主数据库使用其他软件(即MySQL):
app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
我的应用程序不使用复杂的RDBMS功能,例如存储过程,大多数数据库访问情况都是ebean调用,所以它应该兼容MySQL和H2。
然而,evolutions中的表创建语句使用特定于MySQL的功能,例如指定ENGINE = InnoDB
,DEFAULT CHARACTER SET = utf8
等。我担心如果我将删除CREATE TABLE
的所有专有部分,MySQL将使用一些我无法控制的默认设置,这取决于版本,因此要测试和开发应用程序主MySQL配置必须进行修改。
任何人都使用这种方法(使evolutions与MySQL和H2兼容)?
其他想法如何处理:
create table
中的其他MySQL内容(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);
看起来evolution在这里工作,但在每次测试之前如何最好清理数据库?通过创建截断每个表的自定义代码?如果它会丢弃表格,那么演化会在下一次测试之前再次运行,还是每play test
个命令应用一次?或者每Helpers.fakeApplication()
次调用一次?
这里的最佳做法是什么?听说dbunit,是否有可能在没有太多痛苦和怪癖的情况下整合它?
答案 0 :(得分: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);
此代码可放置在您可以继承测试的基类中(或在可以与Runner
注释一起使用的自定义@RunWith
内)。
它还允许您轻松自动化FakeApplication
创建,避免使用一些样板代码。
一些有用的链接:
答案 1 :(得分: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()
和Helpers.stop()
,因此我直接在@Before
和@After
中调用这些方法。
决定不使用H2进行运行测试:是的,运行速度更快,但它与MySQL之间存在太大差异。
答案 2 :(得分:1)
任何人都使用这种方法(使evolutions与MySQL和H2兼容)?
我找到了MySQL特定功能的答案:How can I unit test for MySQL database with Play 2.x?
答案 3 :(得分: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
}
});
}
答案 4 :(得分:1)
你也可以使用DB mock,如果目标是验证你的Slick | JPA | Anorm映射和&amp;基于。的功能。
当它适合时,它更有利于单元测试而不是测试数据库,并且更易于管理(不是设置/清除任务,不是同步测试以避免访问相同的测试表)。
您可以查看我的框架Acolyte(http://github.com/cchantep/acolyte),它在Anorm本身的规范中使用(例如https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/SqlResultSpec.scala)。