如何从基础模拟函数?

时间:2018-05-20 19:17:06

标签: r mocking testthat

我在代码中调用base函数,我想在testthat单元测试中模拟这个函数。

我该怎么做?

library(testthat)

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

my.func()
# sysname 
# "Linux" 

test_that("base function can be mocked",
  with_mock(
    Sys.info = function() return(list(sysname = "Clever OS")),  # see edit 2 !!!
    expect_equal(my.func(), "Clever OS", fixed = TRUE)
  )
)
# Error: Test failed: 'base function can be mocked'
# * my.func() not equal to "Clever OS".

?with_mock说:

  

无法模拟基础包中的函数,但这可以起作用   通过定义包装函数轻松实现。

我可以通过我从Sys.info()调用的包装函数将基函数调用封装到my.func然后让我们假设我不能这样做,因为我正在测试一个我无法更改的包中的函数...

对此有何解决方案?

我在Ubuntu 14.04上使用R3.4.4 64位,testthat为2.0.0.9000。

修改1:

使用

`base::Sys.info` = function() return(list(sysname = "Clever OS"))

导致testthat错误消息:

  

无法在基础包(基础)中模拟函数

编辑2:由于@suren在他的回答中显示我的代码示例在这里是错误的(模拟函数返回另一个类然后是原始类: - (

正确的模拟函数应该是:Sys.info = function() return(c(sysname = "Clever OS"))

2 个答案:

答案 0 :(得分:6)

错误消息 my.func()不等于“Clever OS”。。 原因是Sys.info返回一个命名的字符向量,而模拟函数为list

只需修改模拟函数和期望值即可:

test_that("base function can be mocked",
  with_mock(
    Sys.info = function() return(c(sysname = "Clever OS")),
    expect_equal(my.func(), c(sysname = "Clever OS"), fixed = TRUE)
  )
)

这甚至可以在包中使用。

注意:根据with_mock的帮助,基本功能的模拟不应该起作用,但它确实(至少在目前)。

以下(my.func()$`sysname`)似乎使用问题中的原始代码传递测试。

test_that("base function can be mocked",
          with_mock(
            Sys.info = function() return(list(sysname = "Clever OS")), 
            expect_equal(my.func()$`sysname`, "Clever OS", fixed = TRUE)
          )
)

或者,列出expect_equal

中的字符串
test_that("base function can be mocked",
          with_mock(
            Sys.info = function() return(list(sysname = "Clever OS")),  
            expect_equal(my.func(), list(`sysname` = "Clever OS"), fixed = TRUE)
          )
)

答案 1 :(得分:3)

关于使用with_mock模拟基本函数的警告:

即使基本函数的模拟可能在我的问题和@Suren的接受答案中有效,但with_mock包中testthat的许多问题都不鼓励模拟基本包函数(甚至是外部函数)被测试的包裹),e。克。

testthat 2.0.0 - Breaking API changes

  • &#34;不能在基础包中模拟函数&#34;:你不能再使用with_mock()来模拟基础包中的函数,因为这不再适用于R-devel字节码编译器。 我建议改为使用mockerymockr

Don't allow base packages to be mocked

with_mock() function seems to interact badly with the JIT compiler

  • 正如我在hadley / testthat#546(评论)中提到的那样, mockery已经有一个名为stub的with_mock的替换函数,它采用不同的方法来模拟问题的问题在其他线程中提出。如果with_mock中断,我认为这个包可以和嘲弄一样好用。我也认为嘲弄是一个合理的地方,因为遗产的缘故,它可以与存根一起存在。

Prevent with_mock from touching base R packages