在R中如何使用withr::defer()替代已弃用的testthat::setup()?
testthat v3 替代旧版setup()的实现方案
针对你需要在单个测试文件内仅执行一次耗时数据生成、供所有测试用例复用的需求,搭配withr::defer的实现方式如下,可完全对齐旧版setup()的使用效果:
代码实现
直接在测试文件的最外层作用域编写生成逻辑即可:
# 测试文件开头,该逻辑仅会在当前文件加载时执行1次 data <- matrix(rnorm(9), 3, 3) # 如果需要实现原teardown()的资源清理效果(比如关闭数据库连接、删除临时文件),再搭配withr::defer使用 # withr::defer({ # # 此处写资源释放逻辑 # rm(data) # dbDisconnect(conn) # }, envir = testthat::get_test_file_env())
后续测试用例无需额外修改,直接使用预生成的data对象即可:
# 第一个测试 test_that("校验数据行数量", { expect_equal(nrow(data), 3) }) # 第二个测试 test_that("校验数据列数量", { expect_equal(ncol(data), 3) })
原理解释
- testthat v3中单个测试文件的所有代码会在独立的文件环境中运行,
test_that块外定义的变量默认对当前文件所有测试用例可见,且仅执行一次,原生匹配旧版setup()的核心需求。 - 仅当你需要在当前文件所有测试执行完毕后自动释放资源时,才需要调用
withr::defer,指定环境参数为当前测试文件的环境,清理逻辑就会在全文件测试结束后自动触发。 - 若你只需要复用固定测试数据、无后续清理需求,甚至不需要显式调用
withr::defer,直接在测试文件外层生成数据即可。
单测试用例独立生成数据的场景
如果后续有需求要求每个测试用例拿到互不干扰的全新数据,可以封装为固件函数:
# 定义可复用的测试数据固件 create_test_data <- function() { data <- matrix(rnorm(9), 3, 3) # 绑定清理逻辑到调用测试用例的环境,用例跑完自动清理 withr::defer(rm(data), envir = parent.frame()) return(data) } test_that("校验数据行数量", { data <- create_test_data() expect_equal(nrow(data), 3) }) test_that("校验数据列数量", { data <- create_test_data() expect_equal(ncol(data), 3) })
该写法下每个测试用例都会重新执行一次数据生成逻辑,完全避免用例之间的状态干扰。
内容的提问来源于stack exchange,提问作者Mihai Constantin
相关产品推荐
相关产品推荐

