【问题标题】:testthat: set up database connection available to all teststestthat:设置可用于所有测试的数据库连接
【发布时间】:2019-10-21 18:50:07
【问题描述】:

我的 R 包修改了远程数据库中的数据,我想用testthat 编写一些测试。

我知道我可以模拟数据库,但我宁愿简单地使用我们的开发数据库。

如何使数据库连接对所有需要它的测试可用,同时确保创建的任何连接都被破坏?很明显,连接应该发生在 setup 中,而断开连接应该发生在 teardown 中,但我没有管理。

我尝试将以下代码放在tests/testthat.R 或帮助文件tests/testthat/helper-_ 中,但无济于事。

setup({
  # db_connect is just a basic wrapper around RMariaDB::dbConnect with logging
  db_con <- db_connect(conf$database, loglevel = "none")
})

teardown({
  # db_connect is just a basic wrapper around DBI::dbDisconnect with logging
  db_disconnect(db_con = db_con, loglevel = "none")
})

我最初的测试是:

tests
├── testthat
│   ├── helper-_.R
│   ├── test-connect.R
│   └── test-questions.R
└── testthat.R

在第一个文件(所有测试都通过)之后,我得到了Error in DBI::dbDisconnect(db_con) : object 'db_con' not found,这表明正在拆除,但没有找到db_con

之后,所有需要db_con 的测试都会失败并显示object 'db_con' not found

我是否必须为每个需要db_con 的文件创建一个帮助文件?还是我必须明确地获取一个通用的帮助文件?

有没有一种方法可以让我在某个地方建立一次连接,然后让它可用于所有测试并在最后销毁?

【问题讨论】:

    标签: r database-connection testthat


    【解决方案1】:

    来自testthatdocs

    setup() 块中的代码会立即在干净的环境中运行

    我相信这意味着如果你想保存在 setup 环境中创建的任何对象,那么你需要将它们放在全局环境中

    setup({
      db_con <- db_connect(conf$database, loglevel = "none")
      assign("db_con", db_con, envir = .GlobalEnv)
    })
    

    然后在你的teardown()方法中,它就能找到连接

    teardown({
      db_disconnect(db_con = db_con, loglevel = "none")
      # Can also remove it from the global environment after disconnect
      rm(db_con, envir = .GlobalEnv)
    })
    

    混入全局环境并不理想,但只要仔细命名并在完成后将其删除,这应该不是问题。

    似乎setup() 被设计用于读取/写入临时文件/临时目录,而不是用于创建供所有测试使用的全局对象,但我可能弄错了。

    我在研究这个问题时遇到的一个有用的例子:https://github.com/ropensci/Rpolyhedra/blob/3675a3a6eb8b2807f26fb2ebc929b9f5072681db/tests/testthat/test_package_lib.R#L7

    【讨论】:

      【解决方案2】:

      编辑 21-07-07:截至 2021 年 1 月 14 日,有包 pool 以下列方式解决此问题:

      pool <- dbPool(
        drv = RMySQL::MySQL(),
        dbname = "shinydemo",
        host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com",
        username = "guest",
        password = "guest"
      )
      

      pool 包的目标是抽象出连接管理的逻辑和从远程数据库获取新连接的性能成本


      EDIT : test-connect_init 中的 dbDisconnect。这种结构最适合从数据库(一次或几次)获取数据的工作流。

      免责声明:以下内容已通过 Impala 成功测试。

      我选择了采购方式,创建了一个在脚本和测试中调用的 connect_init.R 函数:

      组织

      R
      ├── utils
      |   ├── connect_init.R
      |   ├── df_import.R
      ├── clean
      |   ├── data_clean.R
      tests
      ├── testthat
      │   ├── test-connect.R
      │   ├── test-import.R
      │   └── test-clean.R
      └── testthat.R
      

      进程

      connect_init.R

      connect_init <- function(params) DBI::dbConnect(...)
      

      data_clean.R

      [...]
      con <- connect_init(params)
      rqt <- "select * from db.tab"
      dframe <- DBI::dbGetQuery(conn = con, rqt)
      
      # --- when import finished
      DBI::dbDisconnect(con)
      

      测试

      test-connect.R

      context("test-connect")
      
      test_that("connexion to Impala doable",
              res <- mypkg::connect_init(params)
              testthat::expect_true(attributes(res)$class[1] == "Impala")
              DBI::dbDisconnect(res)
      })
      

      测试导入.R

      context("test-import")
      
      test_that("import from Impala doable", {
      
              res <- mypkg::df_import(paramsbis)
      
              testthat::expect_s3_class(object = res, class = "data.frame")
              testthat::expect_true(nrow(res) > 0)
      })
      

      然后在其他测试中使用时打开和关闭连接。我对测试此部分的其他方式非常感兴趣+关于如何改进这部分的反馈。

      我们是否应该存储最少的样本数据,以便在出现网络/数据库问题时非连接测试不会失败?

      【讨论】:

      • 所以,换句话说,您在每个测试中手动连接和断开连接?如果是,则必须有更好的方法...同样在这种情况下,如果其中一个测试失败,则连接保持打开状态(而即使测试失败,teardown 也始终运行)
      • 你是对的:-我需要在 test-connect 中添加一个断开连接(它包含在 test-import 中)。 - 此处建议的程序未针对您的工作流程进行优化(我在从 Impala 导入一次(或几次)数据时使用它)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-23
      • 2017-09-07
      相关资源
      最近更新 更多