将模拟与 apply R 结合使用

Using mocking with apply R

我目前正在使用包 testthatmockery 在一些单元测试中进行模拟。我试图了解当使用 apply 在函数中实际调用模拟函数时,mockery 包中的函数 expect_args 是如何工作的。下面是测试成功的例子

myMean <- function(A){
 apply(A,1,mean)
}

myMat = matrix(rep(1,6), nrow = 2, ncol = 3)
test_that("myMean calls base::mean correctly",{
 m <- mock(1, cycle = TRUE)
 with_mock(
 `base::mean` = m,
  myMean(myMat),
  expect_args(m, 1, as.double(myMat[1,])))
})

我们再举一个稍微复杂一点的例子,myMean的参数实际上是一个data.frame,需要在函数内部转为矩阵。

myMean <- function(A){
 B = as.matrix(A)
 apply(B,1,mean)
}

myMat = as.data.frame(myMat)
test_that("myMean calls base::mean correctly",{
m <- mock(1, cycle = TRUE)
with_mock(
 `base::mean` = m,
  myMean(myMat),
 expect_args(m, 1, as.double(myMat[1,])))
})

然后我收到以下错误消息:

Error: Test failed: 'myMeanSimple calls base::mean correct number of times         
* 1st actual argument not equal to 1st expected argument.
names for target but not for current

此错误在 mockery 包的 vignette 中有解释。然而,我没有找到我应该与 as.double(myMat[1,]) 相关联的参数名称。

首先,我很高兴这个小工具变得有用!其次,您看到的错误源于您的转换是如何进行的以及 expect_args 如何比较结果。在内部,我们调用 expect_equal,它要求 matrix 的所有名称都出现在那里。

调用你的第二个例子后我运行这个:

> mock_args(m)
[[1]]
[[1]][[1]]
V1 V2 V3 
 1  1  1 

[[2]]
[[2]][[1]]
V1 V2 V3 
 1  1  1 

所以你可以看到在第一次调用中传递了一个named raw,第二次调用也是如此——有names分配给每一列。这是因为 as.matrix 保留了列名。所以这不是关于 argument 名称,这是关于比较的 data 中的名称。

现在,当您 运行 使用 expect_args 进行最终比较时,您实际上使用的是 as.double,它不保留名称。因此,您看到的错误。要修复它,您只需将您的期望更改为:

expect_args(m, 1, as.matrix(myMat)[1,])

希望这能解决您的问题。