我撰寫了一個從 aws s3-bucket 獲取單個檔案的匯入函式。該函式本身是一個包裝器aws.s3::s3read_using(),它將讀取函式作為其第一個引數。
我為什么要繞aws.s3::s3read_using()?因為我需要做一些特殊的錯誤處理,并希望包裝函式做一些Recall()限制......但那是另一回事。
現在我已經成功構建并測驗了我的包裝功能,我想再做一個包裝:
我想在我的包裝器上迭代 n 次以將下載的檔案系結在一起。我現在已經到手的“reading_function”到困難FUN的說法aws.s3::s3read_using()。
我可以通過簡單地使用...- 但是!我想向我的包裝包裝器的用戶說明,他需要指定該引數。
所以我決定使用 rlangsrlang::enexpr()來捕獲引數并將其交給我的第一個包裝器 via !!- 作為回報,rlang::enexpr()它再次捕獲該引數并將其移交 - 最后 - 到aws.s3::s3read_using()viarlang::expr(aws.s3::s3read_using(FUN = !!reading_fn, object = s3_object))
這作業得非常好和順利。我的問題是使用testthat和測驗該函式構造mockery
這是一些廣泛簡化的代碼:
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)
uj5u.com熱心網友回復:
我認為你在這里讓事情復雜化了,盡管我可能不完全理解你的最終目標。您可以直接通過引數傳遞函式而不會出現任何問題。您上面的示例代碼可以很容易地簡化為(保持回圈只是為了匹配您的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在用戶提供運算式而不僅僅是函式時使用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/379992.html
