使用 H2 内存数据库设置 Scala Play 测试

Posted

技术标签:

【中文标题】使用 H2 内存数据库设置 Scala Play 测试【英文标题】:Setting up Scala Play testing with H2 in-memory database 【发布时间】:2016-09-18 15:05:12 【问题描述】:

Play 文档说“内存数据库中的 H2 非常便于开发,因为当游戏重新启动时,你的进化是从头开始运行的。”这就是我想要的。这是我到目前为止所做的(播放 2.5.6):

    我创建了一个测试配置文件以使用带有进化的 H2,如下:

    play.evolutions 
      db.default.enabled = true
      autoApply = true
    
    
    db 
      default 
        driver = org.h2.Driver
        url = "jdbc:h2:mem:test;MODE=mysql;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1"
        username = sa
        password = ""
        pool = "bonecp"   // otherwise you can't log your sql...
        bonecp.logStatements=true
      
    
    

    我添加了一个简单的进化文件“/conf/evolutions/default/1.sql”:

    # --- !Ups
    
    CREATE TABLE PROJECTS (
      id int(11) NOT NULL
    ) ;
    
    # --- !Downs
    
    DROP TABLE IF EXISTS PROJECTS;
    

    我的控制器需要一个数据库。这是应该注入测试数据库而不是 MySQL 的地方。它有一个读取表格的测试操作:

    class DataManagementController @Inject()(db: Database) extends Controller 
    
          def test() = Action 
            db.withConnection  conn =>
              val st = conn.createStatement()
              val res = st.executeQuery("SELECT * FROM PROJECTS")
              while (res.next())  println(res.getInt("id")) 
            
            Ok("")
          
    
    

    我对该操作的第一个测试如下:

    class ControllerSpec extends PlaySpec with OneAppPerSuite 
    
      val TestDb = Databases.inMemory("default")
      val dataCtrl = new DataManagementController(TestDb)
    
      "DataManagementController" should 
        "test" in 
          dataCtrl.test().apply(FakeRequest())
        
      
    
    

    当我运行测试时,我看到进化被应用于 H2 数据库,但在运行查询时,尽管有 DB_CLOSE_DELAY=-1 选项,但所有内容都被删除了 (?):

    ~ test-only ControllerSpec
    
    [info] ControllerSpec:
    [debug] c.j.b.BoneCPDataSource - JDBC URL = jdbc:h2:mem:test;MODE=MYSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1, Username = sa, partitions = 1, max (per partition) = 30, min (per partition) = 5, idle max age = 10 min, idle test period = 1 min, strategy = DEFAULT
    [debug] c.j.b.StatementHandle - select id, hash, apply_script, revert_script, state, last_problem from play_evolutions where state like 'applying_%'
    [error] o.j.StatementLogger - java.sql.Statement.executeQuery: select id, hash, apply_script, revert_script, state, last_problem from play_evolutions where state like 'applying_%';
    throws exception: org.h2.jdbc.JdbcSQLException: Table "play_evolutions" not found; SQL statement:
    select id, hash, apply_script, revert_script, state, last_problem from play_evolutions where state like 'applying_%' [42102-192]
    [...]
    [debug] c.j.b.StatementHandle -
      create table play_evolutions (
    [...]
    [debug] c.j.b.StatementHandle - CREATE TABLE PROJECTS (
        id int(11) NOT NULL AUTO_INCREMENT,
        person_id int(11) NOT NULL,
        name varchar(255)  NOT NULL,
        PRIMARY KEY (id),
    )
    [debug] c.j.b.StatementHandle - update play_evolutions set state = 'applied' where id = 1
    [debug] c.j.b.StatementHandle - select id, hash, apply_script, revert_script, state, last_problem from play_evolutions where state like 'applying_%'
    
    [info] DataManagementController
    [info] application - Creating Pool for datasource 'default'
    [info] - should test *** FAILED ***
    [info]   org.h2.jdbc.JdbcSQLException: Table "PROJECTS" not found;
    [...]
    

    如果我使用 h2-browser (url: "jdbc:h2:mem:test" + args) 探索数据库,它是空的。但是如果我现在修改“1.sql”,我会得到 ​​p>

    Database 'default' is in an inconsistent state![An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.]
    

    但它没有说明如何解决问题(因为我在浏览器中看到一个空数据库)。

对于设置测试数据库的任何帮助,我将不胜感激。我在这里看到很多帖子都问过类似的问题,要么没有答案,要么没有被接受,有几种不同的方法,我确实已经尝试了好几天。注:它会在 *** Docs(和 Play docs btw)中成为一个有用的部分。

【问题讨论】:

也可以看看tour.acolyte.eu.org 【参考方案1】:

我没有检查过 Play 2.5,但我希望这与最新的 2.4 版本非常相似。

在这里了解组件的生命周期很重要。您的测试设置方式实际上涉及两个 H2 数据库。

    test 由 PlaySpec 根据您提供的测试配置进行管理。这通常需要使用WithApplication。这里DB_CLOSE_DELAY 设置正确。

    您正在针对自己创建的 H2 数据库 default 运行测试。 DB_CLOSE_DELAY 没有为此实例设置,但您可以提供 urlOptions 的映射:

    Databases.inMemory("default", urlOptions = Map("DB_CLOSE_DELAY" -> "-1"))

有几个选项(不完整):

    使用普通的 Specs2 规范而不是 PlaySpec。这样您就可以避免在后台运行假的 Play 应用程序。但是您不会从 Play 的演变中受益。

    使用 H2 和 url 选项 INIT=RUNSCRIPT FROM 'classpath:evolutions/default/1.sql' 应用迁移/插入测试夹具

    将规范中的数据库指向 test 而不是 default,以确保您连接到同一个数据库并使用 WithApplication 让 Play 管理它。

    除了在测试中实例化您的控制器之外,您还可以让 Play 的路由器执行此操作并调用操作。查看文档here。

【讨论】:

谢谢,它有很大帮助。那么,我应该放弃拥有 2 个设置文件吗?我不知道测试数据库是否应该在配置文件中定义,在假应用程序中,带有一些 Guice 覆盖,或者像我一样直接实例化。我真的不需要选项,而是使用依赖注入为我未来的所有应用程序一劳永逸地拥有一个测试数据库的最佳方法。 很难给出明确的建议,利弊太多了。使用WithApplication 确实很简单,并且带来了 Play 连接您的应用程序的便利。但是,您一次只能使用一个数据库/因此也只能使用一个 Play 应用程序。因此,您无法并行运行测试,因为您无法隔离它们并且它们可能会发生冲突。就我个人而言,这对我来说很糟糕,但对于一个小应用程序来说,这可能不是什么大问题。如果您正在寻找一种快速的方法,我建议您保留测试配置并尝试 3)。查看文档的链接,这很有帮助... 谢谢。我读了很多次这些链接。选项 3 意味着以某种方式创建一个假的隐式 app(我不知道如何将我的数据库注入该模板,尽管我尝试了几个小时使用 Guice 过于复杂的东西,在 SO 上发布了一个问题但没有得到答案),他们自己说在文档中为每个测试/套件创建一个完整的应用程序实例并不好。 我想我明白你所说的测试配置文件 + 选项 3 的意思。毕竟这可能是最好的。 抱歉,昨天没能再次回复您。惊人的!真正的线索是不需要注入测试数据库,因为它已经配置为使用 H2,并且 play 将为您设置它。

以上是关于使用 H2 内存数据库设置 Scala Play 测试的主要内容,如果未能解决你的问题,请参考以下文章

使用 scala play specs2 针对测试 mysql 数据库的演变

使用H2数据库在游戏中运行测试时,未知数据类型为“JSONB”

在 play 框架中连接到本地 h2 数据库

在 Play2 / Scala 中从内存中的 MultipartFormData 中提取文件

使用 h2-browser 访问 play 项目数据库时用户名错误

内省 H2 内存数据库