R - 嘲弄::嘲笑和嘲弄::存根不能与准报价正常工作?



我编写了一个从 aws s3-bucket 获取单个文件的导入函数。

该函数本身是aws.s3::s3read_using()的包装器,它将读取函数作为其第一个参数。

我为什么要缠绕aws.s3::s3read_using()?因为我需要做一些特殊的错误处理,并希望包装函数做一些Recall()达到极限......但那是另一回事了。

现在我已经成功构建并测试了我的包装函数,我想做另一个包装:

我想在我的包装器上迭代 n 次以将下载的文件绑定在一起。我现在很难将"reading_function"交给aws.s3::s3read_using()FUN论点。

我可以通过简单地使用...来做到这一点 - 但是! 我想向我的包装包装器的用户明确表示,他需要指定该参数。

所以我决定使用 rlangsrlang::enexpr()来捕获参数,并通过!!将其交给我的第一个包装器 - 作为回报,它再次用rlang::enexpr()捕获该参数并将其移交 - 最后 - 通过rlang::expr(aws.s3::s3read_using(FUN = !!reading_fn, object = s3_object))aws.s3::s3read_using()

这工作非常精细和流畅。我的问题是使用testthatmockery测试该函数结构

下面是一些大致简单的代码:

my_workhorse_function <- function(fn_to_work_with, value_to_work_on) {
fn <- rlang::enexpr(fn_to_work_with)
# Some other magic happens here - error handling, condition-checking, etc...
out <- eval(rlang::expr((!!fn)(value_to_work_on)))
}
my_iterating_function <- function(fn_to_iter_with, iterate_over) {
fn <- rlang::enexpr(fn_to_iter_with)
out <- list()
for(i in seq_along(iterate_over)) {
out[[i]] <- my_workhorse_function(!!fn, iterate_over[i])
}
return(out)
}
# Works just fine
my_iterating_function(sqrt, c(9:16))

现在,要测试一下:

# Throws an ERROR: 'Error in `!fn`: invalid argument type'
test_that("my_iterating_function iterates length(iterate_over) times over my_workhorse_function", {
mock_1 <- mockery::mock(1, cycle = TRUE)
stub(my_iterating_function, "my_workhorse_function", mock_1)
expect_equal(my_iterating_function(sqrt, c(9:16)), list(1,1,1,1,1,1,1,1))
expect_called(mock_1, 8)
})

我用过一个工作轮,但这感觉不对,即使它有效:

# Test passed
test_that("my_iterating_function iterates length(iterate_over) times over my_workhorse_function", {
mock_1 <- mockery::mock(1, cycle = TRUE)
stub(my_iterating_function, "my_workhorse_function", 
function(fn_to_work_with, value_to_work_on) {
fn <- rlang::enexpr(fn_to_work_with)
out <- mock_1(fn, value_to_work_on)
out})
expect_equal(my_iterating_function(sqrt, c(9:16)), list(1,1,1,1,1,1,1,1))
expect_called(mock_1, 8)
})

我正在使用R: 4.1.1版本 我正在使用testthat(3.1.1)mockery(0.4.2)rlang(0.4.12)的版本

我认为你在这里使事情复杂化,尽管我可能并不完全理解你的最终目标。您可以通过参数直接传递函数,没有任何问题。上面的示例代码可以轻松简化为(保持循环以匹配您的test_that()调用):

library(testthat)
library(mockery)
my_workhorse_function <- function(fn_to_work_with, value_to_work_on) {
fn_to_work_with(value_to_work_on)
}
my_iterating_function <- function(fn_to_iter_with, iterate_over) {
out <- list()
for(i in seq_along(iterate_over)) {
out[[i]] <- my_workhorse_function(fn_to_iter_with, iterate_over[i])
}
return(out)
}
# Works just fine
my_iterating_function(sqrt, c(9:16))
#> [[1]]
#> [1] 3
#> 
#> ...
test_that("my_iterating_function iterates length(iterate_over) times over my_workhorse_function", {
mock_1 <- mockery::mock(1, cycle = TRUE)
stub(my_iterating_function, "my_workhorse_function", mock_1)
expect_equal(my_iterating_function(sqrt, c(9:16)), list(1,1,1,1,1,1,1,1))
expect_called(mock_1, 8)
})
#> Test passed   

您可以直接通过所有嵌套函数传递FUN。你用enexpr()包装的函数在你显式调用它们之前永远不会被计算出来。通常在用户提供表达式(而不仅仅是函数)时使用enexpr

相关内容

  • 没有找到相关文章

最新更新