代码之家  ›  专栏  ›  技术社区  ›  R Yoda

如何从基础模拟函数?

  •  12
  • R Yoda  · 技术社区  · 7 年前

    我在代码中从基调用一个函数,我想在 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上使用了64位的r3.4.4,测试结果是2.0.0.9000。

    编辑1:

    使用

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

    结果是 测试 错误消息:

    无法模拟基本包(base)中的函数

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

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

    2 回复  |  直到 7 年前
        1
  •  6
  •   R Yoda    7 年前

    错误消息是 my.func()不等于“聪明的操作系统”。 . 原因是 Sys.info 当模拟函数a 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)
              )
    )
    
        2
  •  3
  •   R Yoda    7 年前

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

    即使在我的问题和@suren的公认答案中,对基本函数的模拟也可能有效。 用模拟 在包裹里 testthat 取消对基本包函数的模拟(甚至是测试包外部的函数),例如。

    testthat 2.0.0 - Breaking API changes

    • “不能模拟基本包中的函数”:您不能再与_mock()一起使用来模拟基本包中的函数,因为这在r-devel中不再适用,因为字节代码编译器发生了更改。 我建议使用 mockery mockr 相反。

    Don't allow base packages to be mocked

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

    • 正如我在Hadley/Testthat 546(评论)中提到的, mokery已经有了一个名为stub的with-mock的替换函数,它采用了一种不同的方法来模拟不受限制的事物。 其他线程中提出的问题。如果有了模拟休息,我认为这个包也可以和模拟工作一样好。我还认为mokery是和mock一起居住的一个合理的地方,因为遗留的原因,它可以和stub一起居住。

    Prevent with_mock from touching base R packages