I have some delicate issues with environments that are currently manifesting themselves in my unit tests. My basic structure is this
- I have a main function
mainthat has many arguments wrapperis a wrapper function (one of many) that pertains only to selected arguments ofmainhelperis an intermediate helper function that is used by all wrapper functions
I use eval and match.call() to move between wrappers and the main function smoothly. My issue now is that my tests work when I run them line by line, but not using test_that().
Here is a MWE that shows the problem. If you step through the lines in the test manually, the test passes. However, evaluating the whole test_that() chunk the test fails because one of the arguments can not be found.
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)
})
With quite some confidence, I suspect I need to modify the default environment used by eval (i.e. parent.frame()), but I am not sure how to do this.
You want to evaluate your call in your parent environment, not the local function environment. Change your helper to
This is assuming that
helperis always called withinwrapperwhich is called from somewhere else the parameters are defined.It's not clear in this case that you really need all this non-standard evaulation. You might also consider a solution like
Here
wrapperjust bundles all it's parameters values into a list. Then you can just pass a list of parameters tohelperanddo.callwill pass that list as parameters to yourmainfunction. This will evaluate the parameters ofwrapperwhen you call it do you don't have to worry about the execution evironment.