with_mocked_bindings()
and local_mocked_bindings()
provide tools for
"mocking", temporarily redefining a function so that it behaves differently
during tests. This is helpful for testing functions that depend on external
state (i.e. reading a value from a file or a website, or pretending a package
is or isn't installed).
These functions represent a second attempt at bringing mocking to testthat, incorporating what we've learned from the mockr, mockery, and mockthat packages.
local_mocked_bindings(..., .package = NULL, .env = caller_env())with_mocked_bindings(code, ..., .package = NULL)
Name-value pairs providing functions to mock.
The name of the package where mocked functions should be
inserted. Generally, you should not need to supply this as it will be
automatically detected when whole package tests are run or when there's
one package under active development (i.e. loaded with
pkgload::load_all()
).
Environment that defines effect scope. For expert use only.
Code to execute with specified bindings.
There are four places that the function you are trying to mock might come from:
Internal to your package.
Imported from an external package via the NAMESPACE
.
The base environment.
Called from an external package with ::
.
They are described in turn below.
You mock internal and imported functions the same way. For example, take this code:
some_function <- function() {
another_function()
}
It doesn't matter whether another_function()
is defined by your package
or you've imported it from a dependency with @import
or @importFrom
,
you mock it the same way:
local_mocked_bindings(
another_function = function(...) "new_value"
)
Note that it's not possible to mock functions in the base namespace (i.e. functions that you can use without explicitly importing them) since currently we don't know of a way to to mock them without potentially affecting all running code. If you need to mock a base function, you'll need to create a wrapper, as described below.
It's trickier to mock functions in other packages that you call with ::
.
For example, take this minor variation:
some_function <- function() {
anotherpackage::another_function()
}
To mock here, you'll need to modify another_function()
inside the
anotherpackage
package. You can do this by supplying the .package
argument:
local_mocked_bindings(
another_function = function(...) "new_value",
.package = "anotherpackage"
)
But it's not a great idea to mock a namespace that you don't own because it affects all code in that package, not just code in your package. Instead, it's safer to either import the function into your package, or make a wrapper that you can mock:
some_function <- function() {
my_wrapper()
}
my_wrapper <- function(...) {
anotherpackage::another_function(...)
}local_mocked_bindings(
my_wrapper = function(...) "new_value"
)