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
main
that has many arguments wrapper
is a wrapper function (one of many) that pertains only to selected arguments ofmain
helper
is 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
helper
is always called withinwrapper
which 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
wrapper
just bundles all it's parameters values into a list. Then you can just pass a list of parameters tohelper
anddo.call
will pass that list as parameters to yourmain
function. This will evaluate the parameters ofwrapper
when you call it do you don't have to worry about the execution evironment.