Title: | Mocking Library for R |
---|---|
Description: | The two main functionalities of this package are creating mock objects (functions) and selectively intercepting calls to a given function that originate in some other function. It can be used with any testing framework available for R. Mock objects can be injected with either this package's own stub() function or a similar with_mock() facility present in the 'testthat' package. |
Authors: | Noam Finkelstein [aut], Lukasz Bartnik [aut], Jim Hester [aut], Hadley Wickham [aut, cre] |
Maintainer: | Hadley Wickham <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.4.4.9000 |
Built: | 2024-10-25 03:00:04 UTC |
Source: | https://github.com/r-lib/mockery |
Together with mock
can be used to verify whether the
call expression (expect_call
) and/or argument values
(expect_args
) match the expected.
expect_call(mock_object, n, expected_call) expect_args(mock_object, n, ...) expect_called(mock_object, n)
expect_call(mock_object, n, expected_call) expect_args(mock_object, n, ...) expect_called(mock_object, n)
mock_object |
A |
n |
Call number or total number of calls. |
expected_call |
Expected call expression; will be compared unevaluated. |
... |
Arguments as passed in a call. |
With expect_called
you can check how many times has the mock
object been called.
library(testthat) # expect call expression (signature) m <- mock() with_mock(summary = m, summary(iris)) # it has been called once expect_called(m, 1) # the first (and only) call's arguments matches summary(iris) expect_call(m, 1, summary(iris)) # expect argument value m <- mock() a <- iris with_mock(summary = m, summary(object = a)) expect_args(m, 1, object = a) # is an equivalent to ... expect_equal(mock_args(m)[[1]], list(object = a))
library(testthat) # expect call expression (signature) m <- mock() with_mock(summary = m, summary(iris)) # it has been called once expect_called(m, 1) # the first (and only) call's arguments matches summary(iris) expect_call(m, 1, summary(iris)) # expect argument value m <- mock() a <- iris with_mock(summary = m, summary(object = a)) expect_args(m, 1, object = a) # is an equivalent to ... expect_equal(mock_args(m)[[1]], list(object = a))
Mock object's primary use is to record calls that are made on the mocked function.
mock(..., cycle = FALSE, envir = parent.frame()) mock_args(m) mock_calls(m) ## S3 method for class 'mock' length(x)
mock(..., cycle = FALSE, envir = parent.frame()) mock_args(m) mock_calls(m) ## S3 method for class 'mock' length(x)
... |
Values returned upon subsequent calls. |
cycle |
Whether to cycle over the return values. If |
envir |
Where to evaluate the expressions being returned. |
m |
A |
x |
A |
Optionally values/expressions can be passed via ...
for the
mock object to return them upon subsequent calls. Expressions are
evaluated in environment envir
before being returned. If no
value is passed in ...
then NULL
is returned.
Passing an expression or a function call via ...
is also a
way to implement side effects: keep track of the state of code
under testing, throw an exception when a condition is met, etc.
mock_calls
and mock_args
can be used to access the
list of calls made on a mocked function and a respective list of
values of arguments passed to each of these calls.
mock()
returns a mocked function which can be then used
with with_mock
.
mock_args()
returns a list
of list
s
of argument values.
mock_calls()
returns a list
of call
s.
length.mock()
returns the number of calls invoked on m
.
library(testthat) m <- mock(1) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_called(m, 1) expect_call(m, 1, summary(iris)) expect_args(m, 1, iris) }) # multiple return values m <- mock(1, "a", sqrt(3)) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_equal(summary(iris), "a") expect_equal(summary(iris), 1.73, tolerance = .01) }) # side effects m <- mock(1, 2, stop("error")) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_equal(summary(iris), 2) expect_error(summary(iris), "error") }) # accessing call expressions m <- mock() m(x = 1) m(y = 2) expect_equal(length(m), 2) calls <- mock_calls(m) expect_equal(calls[[1]], quote(m(x = 1))) expect_equal(calls[[2]], quote(m(y = 2))) # accessing values of arguments m <- mock() m(x = 1) m(y = 2) expect_equal(length(m), 2) args <- mock_args(m) expect_equal(args[[1]], list(x = 1)) expect_equal(args[[2]], list(y = 2))
library(testthat) m <- mock(1) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_called(m, 1) expect_call(m, 1, summary(iris)) expect_args(m, 1, iris) }) # multiple return values m <- mock(1, "a", sqrt(3)) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_equal(summary(iris), "a") expect_equal(summary(iris), 1.73, tolerance = .01) }) # side effects m <- mock(1, 2, stop("error")) with_mock(summary = m, { expect_equal(summary(iris), 1) expect_equal(summary(iris), 2) expect_error(summary(iris), "error") }) # accessing call expressions m <- mock() m(x = 1) m(y = 2) expect_equal(length(m), 2) calls <- mock_calls(m) expect_equal(calls[[1]], quote(m(x = 1))) expect_equal(calls[[2]], quote(m(y = 2))) # accessing values of arguments m <- mock() m(x = 1) m(y = 2) expect_equal(length(m), 2) args <- mock_args(m) expect_equal(args[[1]], list(x = 1)) expect_equal(args[[2]], list(y = 2))
There are great tools for unit testing in R out there already but they don't come with a lot of support for mock objects. This package aims at fixing that.
library(mockery) m <- mock(TRUE, FALSE, TRUE) # this will make summary call our mock function rather then # UseMethod; thus, summary() will return values as above stub(summary, 'UseMethod', m) summary(iris) # returns TRUE summary(cars) # returns FALSE summary(co2) # returns TRUE ## Not run: library(testthat) m <- mock(TRUE) f <- function() read.csv('data.csv') with_mock(read.csv = m, { f() expect_call(m, 1, read.csv('data.csv')) }) ## End(Not run)
library(mockery) m <- mock(TRUE, FALSE, TRUE) # this will make summary call our mock function rather then # UseMethod; thus, summary() will return values as above stub(summary, 'UseMethod', m) summary(iris) # returns TRUE summary(cars) # returns FALSE summary(co2) # returns TRUE ## Not run: library(testthat) m <- mock(TRUE) f <- function() read.csv('data.csv') with_mock(read.csv = m, { f() expect_call(m, 1, read.csv('data.csv')) }) ## End(Not run)
The result of calling stub
is that, when where
is invoked and when it internally makes a call to what
,
how
is going to be called instead.
stub(where, what, how, depth = 1)
stub(where, what, how, depth = 1)
where |
Function to be called that will in turn call
|
what |
Name of the function you want to stub out (a
|
how |
Replacement function (also a |
depth |
Specifies the depth to which the function should be stubbed |
This is much more limited in scope in comparison to
with_mock
which effectively replaces
what
everywhere. In other words, when using with_mock
and regardless of the number of intermediate calls, how
is
always called instead of what
. However, using this API,
the replacement takes place only for a single function where
and only for calls originating in that function.
f <- function() TRUE g <- function() f() stub(g, 'f', FALSE) # now g() returns FALSE because f() has been stubbed out g() # you can stub multiple functions by calling stub() multiple times f <- function() TRUE g <- function() TRUE h <- function() any(f(), g()) stub(h, 'f', FALSE) stub(h, 'g', FALSE) # now h() returns FALSE because both f() and g() have been stubbed out h()
f <- function() TRUE g <- function() f() stub(g, 'f', FALSE) # now g() returns FALSE because f() has been stubbed out g() # you can stub multiple functions by calling stub() multiple times f <- function() TRUE g <- function() TRUE h <- function() any(f(), g()) stub(h, 'f', FALSE) stub(h, 'g', FALSE) # now h() returns FALSE because both f() and g() have been stubbed out h()