Mocking allows you to temporary replace the implementation of functions within a package, which useful for testing code that relies on functions that are slow, have unintended side effects or access resources that may not be available when testing.

Up until recently, such capability was offered via testthat::with_mock(), but with release of version 3.0.0 and introduction of edition 3, this was deprecated from 'testthat', leaving it to third party packages to replace this feature. Powered by utils::assignInNamespace(), this mocking implementation can be used to stub out both exported and non-exported functions from a package, as well as functions explicitly imported from other packages using either importFrom directives or namespaced function calls using ::.

with_mock(..., mock_env = pkg_env(), eval_env = parent.frame())

local_mock(
  ...,
  mock_env = pkg_env(),
  eval_env = parent.frame(),
  local_env = eval_env
)

Arguments

...

Named parameters redefine mocked functions, unnamed parameters will be evaluated after mocking the functions.

mock_env

The environment in which to patch the functions, defaults to either the package namespace when the environment variable TESTTHAT_PKG is set pr the calling environment. A string is interpreted as package name.

eval_env

Environment in which expressions passed as ... are evaluated, defaults to base::parent.frame().

local_env

Passed to withr::defer() as envir argument (defaults to the values passed as eval_env)

Value

The result of the last unnamed argument passed as ... (evaluated in the environment passed as eval_env) in the case of local_mock() and a list of functions or mock_fun objects (invisibly) for calls to local_mock().

Details

Borrowing the API from the now-deprecated testthat::with_mock(), named arguments passed as ... are used to define functions to be mocked, where names specify the target functions and the arguments themselves are used as replacement functions. Unnamed arguments passed as ... will be evaluated in the environment specified as eval_env using the mocked functions. Functions to be stubbed should be specified as they would be used in package core. This means that when a function from a third party package is imported, prefixing the function name with pkg_name:: will not give the desired result. Conversely, if the function is not imported, the package prefix is of course required. On exit of with_mock(), the mocked functions are reverted to their original state.

Replacement functions can either be specified as complete functions, or as either quoted expressions, subsequently used as function body or objects used as return values. If functions are created from return values or complete function bodies, they inherit the signatures from the respective functions they are used to mock, alongside the ability to keep track of how they are subsequently called. A constructor for such mock-objects is available as mock(), which quotes the expression passed as expr.

If mocking is desirable for multiple separate calls to the function being tested, local_mock() is available, which holds onto the mocked state for the lifetime of the environment passed as local_env using withr::defer(). Unlike with_mock(), which returns the result of evaluating the last unnamed argument passed as ..., local_mock() (invisibly) returns the functions used for mocking, which if not fully specified as functions, will be mock-objects described in the previous paragraph.

Examples


url <- "https://eu.httpbin.org/get?foo=123"
mok <- function(...) "mocked request"

with_mock(
  `curl::curl_fetch_memory` = mok,
  curl::curl_fetch_memory(url)
)
#> [1] "mocked request"

dl_fun <- function(x) curl::curl_fetch_memory(x)

with_mock(
  `curl::curl_fetch_memory` = mok,
  dl_fun(url)
)
#> [1] "mocked request"

with_mock(
  `curl::curl_fetch_memory` = "mocked request",
  dl_fun(url)
)
#> [1] "mocked request"

dl <- function(x) curl::curl(x)

local({
  mk <- local_mock(`curl::curl` = "mocked request")
  list(dl(url), mock_arg(mk, "url"))
})
#> [[1]]
#> [1] "mocked request"
#> 
#> [[2]]
#> [1] "https://eu.httpbin.org/get?foo=123"
#>