You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在testthat单元测试中Mock基础包中的函数?

这个问题确实挺棘手的——testthat默认不让mock基础包的函数,又不能改被测代码。不过有几个不用修改原函数的解决方案,适合你的场景:

方案1:临时向函数环境注入Mock实现

R的函数会优先从自身绑定的环境里查找调用的函数,我们可以把Mock版的Sys.info临时放到my.func的环境中,测试结束后再清理,完全不会影响原函数的正常逻辑:

library(testthat)

my.func <- function() {
  return(Sys.info()["sysname"])
}

test_that("base function can be mocked via environment injection", {
  # 把Mock的Sys.info注入到my.func的专属环境
  environment(my.func)$Sys.info <- function() c(sysname = "Clever OS")
  # 测试完成后立即清理,避免干扰其他测试用例
  on.exit(rm(Sys.info, envir = environment(my.func)))
  
  expect_equal(my.func(), "Clever OS", fixed = TRUE)
})

方案2:用trace临时替换函数行为

利用R内置的trace函数,我们可以给Sys.info添加一个“退出钩子”,让它在执行时返回我们指定的值,测试结束后取消钩子即可:

test_that("base function can be mocked via trace", {
  # 给Sys.info添加退出钩子,强制返回Mock值
  trace(Sys.info, exit = function() return(c(sysname = "Clever OS")), print = FALSE)
  # 必须在测试结束后取消trace,否则会影响全局环境的Sys.info
  on.exit(untrace(Sys.info))
  
  expect_equal(my.func(), "Clever OS", fixed = TRUE)
})

方案3:用mockery包的stub函数(推荐)

如果可以安装额外包,mockery的stub函数专门解决这类无法直接Mock的场景,它会直接替换目标函数内部对指定函数的调用逻辑,写法更简洁:

# 第一次使用先安装包
# install.packages("mockery")

library(mockery)
library(testthat)

test_that("base function can be mocked via mockery::stub", {
  # 直接指定要替换的函数和Mock实现
  stub(my.func, "Sys.info", function() c(sysname = "Clever OS"))
  expect_equal(my.func(), "Clever OS", fixed = TRUE)
})

额外注意

别忘了你之前踩过的返回类型坑:Sys.info()本身返回的是命名向量,不是列表,所以Mock函数一定要返回c(sysname = "Clever OS"),否则预期值对比会失败。

内容的提问来源于stack exchange,提问作者R Yoda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:14:09