R包测试中使用testthat跨单元测试复用变量避免重复生成高开销对象
R包testthat测试中复用高开销生成对象的规范方案
下面是两种符合testthat开发规范的实现方案,均可以避免提前调用未完成测试的函数,同时减少重复生成对象的时间开销:
方案1:按测试文件顺序执行+运行时缓存
testthat默认按文件名字典顺序执行测试文件,你可以通过命名控制测试执行优先级,仅在create_obj测试完全通过后再缓存对象:
- 重命名测试文件,保证create_obj的测试先执行:
- create_obj的测试文件命名为
test-01-create_obj.R - plot_obj的测试文件命名为
test-02-plot_obj.R
- create_obj的测试文件命名为
- 在
test-01-create_obj.R中完成所有测试后缓存对象:
test_that("create_obj功能正常", { objA <- create_obj(sample) expect_equal(length(objA), 10) # 其他create_obj测试逻辑 # 所有测试通过后,缓存对象到testthat共享测试环境 test_env <- getOption("testthat_env") test_env$cached_objA <- objA # 并行测试场景可改用临时文件存储 # saveRDS(objA, file.path(tempdir(), "cached_objA.RDS")) })
- 在
test-02-plot_obj.R中直接读取缓存对象使用:
test_that("plot_obj生成正确绘图", { # 读取缓存对象 objA <- getOption("testthat_env")$cached_objA # 并行场景读临时文件 # objA <- readRDS(file.path(tempdir(), "cached_objA.RDS")) # 你的plot_obj测试逻辑,例如用vdiffr校验绘图输出 vdiffr::expect_doppelganger("objA默认绘图", plot_obj(objA)) })
方案2:使用fixture封装缓存逻辑(适合testthat 3.0+版本)
你可以将对象生成+合法性校验逻辑封装为可复用的fixture,仅在第一次被调用时执行生成和测试逻辑,后续所有测试调用直接复用缓存对象:
- 在
tests/testthat/setup.R中定义fixture:
fixture_objA <- function() { # 已生成过则直接返回缓存,避免重复执行 if (exists("cached_objA", envir = .GlobalEnv)) { return(.GlobalEnv$cached_objA) } # 生成对象并完成内置校验 objA <- create_obj(sample) expect_equal(length(objA), 10) # 其他create_obj校验逻辑 # 缓存到全局环境 assign("cached_objA", objA, envir = .GlobalEnv) return(objA) }
- 所有需要用到objA的测试直接调用fixture即可:
# 任意测试文件中调用 test_that("plot_obj功能正常", { objA <- fixture_objA() expect_s3_class(plot_obj(objA), "ggplot") })
注意事项
- 并行测试场景下不要使用全局环境缓存,改用临时目录存储RDS文件的方式,避免多进程读写冲突。
- testthat测试会话结束后会自动清理临时目录和测试环境,不会有缓存残留影响后续测试的问题。
内容的提问来源于stack exchange,提问作者JohannesNE
相关产品推荐
相关产品推荐

