R 使用TestThis解决环境问题
我对当前在单元测试中表现出来的环境有一些微妙的问题。我的基本结构是这样的R 使用TestThis解决环境问题,r,testthat,nse,R,Testthat,Nse,我对当前在单元测试中表现出来的环境有一些微妙的问题。我的基本结构是这样的 我有一个main函数main,它有许多参数 wrapper是一个包装函数(众多函数中的一个),它只与main helper是所有包装函数都使用的中间helper函数 我使用eval和match.call()在包装器和主函数之间平滑移动我现在的问题是,当我逐行运行测试时,测试工作正常,但不使用test\u that() 这是一个显示问题的MWE。如果手动单步执行测试中的行,则测试通过。但是,对整个test_()进行评估(
- 我有一个main函数
,它有许多参数main
是一个包装函数(众多函数中的一个),它只与wrapper
main
是所有包装函数都使用的中间helper函数helper
eval
和match.call()
在包装器和主函数之间平滑移动我现在的问题是,当我逐行运行测试时,测试工作正常,但不使用test\u that()
这是一个显示问题的MWE。如果手动单步执行测试中的行,则测试通过。但是,对整个test_()进行评估(
chunk)测试失败,因为找不到其中一个参数
library(testthat)
wrapper <- function(a, b) {
fun_call <- as.list(match.call())
ret <- helper(fun_call)
return(ret)
}
helper <- function(fun_call) {
fun_call[[1]] <- quote(main)
fun_call <- as.call(fun_call)
fun_eval <- eval(as.call(fun_call))
return(fun_eval)
}
main <- function(a, b, c = 1) {
ret <- list(a = a, b = b, c = c)
return(ret)
}
test_that("Test", {
a <- 1
b <- 2
x <- wrapper(a = a, b = b)
y <- list(a = 1, b = 2, c = 1)
expect_equal(x, y)
})
库(testthat)
wrapper您希望在父环境而不是本地函数环境中评估调用。将助手更改为
helper <- function(fun_call) {
fun_call[[1]] <- quote(main)
fun_call <- as.call(fun_call)
fun_eval <- eval.parent(fun_call, n=2)
return(fun_eval)
}
这里的wrapper
只是将它的所有参数值绑定到一个列表中。然后,您可以将参数列表传递给helper
和do。call
将该列表作为参数传递给main
函数。这将在调用时评估包装器的参数。您不必担心执行环境。您希望在父环境而不是本地函数环境中评估调用。将助手更改为
helper <- function(fun_call) {
fun_call[[1]] <- quote(main)
fun_call <- as.call(fun_call)
fun_eval <- eval.parent(fun_call, n=2)
return(fun_eval)
}
这里的wrapper
只是将它的所有参数值绑定到一个列表中。然后,您可以将参数列表传递给helper
和do。call
将该列表作为参数传递给main
函数。这将在调用时评估wrapper
的参数。您不必担心执行环境。在wrapper主体中定义helper可能会起作用。在wrapper主体中定义helper可能会起作用。这似乎很好,但我不明白为什么!eval.parent()
中的默认值是n=1
,eval.parent(expr,n)
是eval(expr,parent.frame(n))的缩写。因此eval.parent(expr,1)
是eval(expr,parent.frame(1))
,但是eval
中的默认环境是parent.frame()
-,parent.frame()
的默认环境是n=1
!因此,tl;dr:eval.parent(expr)
和eval(expr)
不一样吗?(显然不是,但我不明白为什么。)此外,我刚刚意识到,如果在测试中设置a2@hejseb,那么这种情况就会发生故障。首先,需要注意的是,当您将参数传递给函数时,它们会在调用环境中进行计算,但当您有一个默认参数值时,这些都是在不同的功能环境中执行的。因此,parent.frame
在传递给函数时与作为默认参数值时的行为不同。如果helper总是从weapper内部调用,那么您可以做fun\u eval谢谢,我在这里学到了很多<代码>包装器
始终调用帮助程序
,因此可以安全地假定。然而,我不会说我故意拖延评估——这正是我所经历的。最初,我想要一堆包装器,它们都只是修改main
的某些参数。然后我使用了match.call()
,以避免大量硬编码包装,因为所有包装的结构都是相同的,所以我将这些内容放入帮助程序中。我还可以补充一点,在我的情况下,wrapper
也有默认值,我需要跟踪这些值和已被覆盖的值。我使用formals()
实现,并将其传递给助手
,因此我确实认为中间函数是必要的(尽管它带来了一些意想不到的复杂情况!)。这似乎很好,但我不明白为什么!eval.parent()
中的默认值是n=1
,eval.parent(expr,n)
是eval(expr,parent.frame(n))的缩写。因此eval.parent(expr,1)
是eval(expr,parent.frame(1))
,但是eval
中的默认环境是parent.frame()
-,parent.frame()
的默认环境是n=1
!因此,tl;dr:eval.parent(expr)
和eval(expr)
不一样吗?(显然不是,但我不明白为什么。)此外,我刚刚意识到,如果在测试中设置a2@hejseb,那么这种情况就会发生故障。首先,需要注意的是,当您将参数传递给函数时,它们会在调用环境中进行计算,但当您有一个默认参数值时,这些都是在不同的功能环境中执行的。因此,parent.frame
在传递给函数时与作为默认参数值时的行为不同。如果helper总是从weapper内部调用,那么您可以做fun\u eval谢谢,我在这里学到了很多<代码>包装器
始终调用帮助程序
,因此可以安全地假定。然而,我不会说我故意拖延评估——这正是我所经历的。最初,我想要一堆包装器,它们都只是修改main
的某些参数。然后我使用了match.call()
,以避免大量硬编码包装,因为所有包装的结构都是相同的,所以我将这些内容放入帮助程序中。我还可以补充一点,在我的情况下,wrapper
也有默认值,我需要跟踪这些值和已被覆盖的值。我使用formals()
并将其传递给助手
,因此我确信中间函数是必要的(尽管它已经出现)