[med-svn] [r-cran-mockery] 01/02: New upstream version 0.4.1
Andreas Tille
tille at debian.org
Fri Nov 24 16:50:12 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-mockery.
commit 49f56e214313a64fdb4a1b5c5cd365f9a46f0d07
Author: Andreas Tille <tille at debian.org>
Date: Fri Nov 24 17:43:43 2017 +0100
New upstream version 0.4.1
---
DESCRIPTION | 31 +++
LICENSE | 2 +
MD5 | 21 ++
NAMESPACE | 13 ++
NEWS | 12 ++
R/expectations.R | 130 ++++++++++++
R/mock-object.R | 146 +++++++++++++
R/mockery.R | 34 +++
R/stub.R | 169 +++++++++++++++
README.md | 153 ++++++++++++++
build/vignette.rds | Bin 0 -> 226 bytes
inst/doc/mocks-and-testthat.R | 126 +++++++++++
inst/doc/mocks-and-testthat.Rmd | 252 ++++++++++++++++++++++
inst/doc/mocks-and-testthat.html | 434 ++++++++++++++++++++++++++++++++++++++
man/call-expectations.Rd | 55 +++++
man/mock.Rd | 106 ++++++++++
man/mockery.Rd | 37 ++++
man/stub.Rd | 45 ++++
tests/testthat.R | 4 +
tests/testthat/test-mock-object.R | 179 ++++++++++++++++
tests/testthat/test_stub.R | 332 +++++++++++++++++++++++++++++
vignettes/mocks-and-testthat.Rmd | 252 ++++++++++++++++++++++
22 files changed, 2533 insertions(+)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..8cb4324
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,31 @@
+Package: mockery
+Version: 0.4.1
+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 at R: c(
+ person("Noam", "Finkelstein", role = c("aut", "cre"),
+ email = "noam.finkelstein at jhu.edu"),
+ person("Lukasz", "Bartnik", rol = c("aut"),
+ email = "l.bartnik at gmail.com")
+ )
+URL: https://github.com/n-s-f/mockery
+BugReports: https://github.com/n-s-f/mockery/issues
+Imports: testthat
+Suggests: knitr, rmarkdown (>= 1.0)
+License: MIT + file LICENSE
+Collate: 'expectations.R' 'mockery.R' 'mock-object.R' 'stub.R'
+VignetteBuilder: knitr
+RoxygenNote: 6.0.1
+NeedsCompilation: no
+Packaged: 2017-11-10 14:58:33 UTC; noam
+Author: Noam Finkelstein [aut, cre],
+ Lukasz Bartnik [aut]
+Maintainer: Noam Finkelstein <noam.finkelstein at jhu.edu>
+Repository: CRAN
+Date/Publication: 2017-11-10 18:22:21 UTC
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1762254
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2016
+COPYRIGHT HOLDER: Noam Finkelstein, Lukasz Bartnik
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..f234b7c
--- /dev/null
+++ b/MD5
@@ -0,0 +1,21 @@
+e732efce7de7dbcc8724f7f2dbcfb1a8 *DESCRIPTION
+3d598f5220436233590531b1de2f14b4 *LICENSE
+7d0483b34bc263b9accf579e55eafe60 *NAMESPACE
+ab111abd065205444612fd3a6316fb53 *NEWS
+29fab533619bbf0b79f008f4c5d81d3f *R/expectations.R
+6bd1b03af221e687d19b6d9abec4aa01 *R/mock-object.R
+0cfe2a6243fd4bb782f5243bef1681dd *R/mockery.R
+966b285097e526a71e5ab3300c416ba5 *R/stub.R
+69537e7efbb09e3ad6d0e9c0d3da57c6 *README.md
+dc43b969e6b3e7baeb161990a9a9a773 *build/vignette.rds
+03f278824fd4bba9adcb6aa16815e422 *inst/doc/mocks-and-testthat.R
+710dff0033da00ba9514450a0c4f7445 *inst/doc/mocks-and-testthat.Rmd
+30cdc4c20e7e53d9312b1611486c21b8 *inst/doc/mocks-and-testthat.html
+2a44bbe6cf2f5f7f420d88ecc506ce86 *man/call-expectations.Rd
+e8ebb1f75ae4033bec35f85dff80a1e1 *man/mock.Rd
+307d59ba3a7efe7341616c00548a93ab *man/mockery.Rd
+94bfd4dda4facc03b153da7b97336323 *man/stub.Rd
+aa03655779278d76db7f4ee3d7e1712c *tests/testthat.R
+146022703ddd614dc91c4054075d7947 *tests/testthat/test-mock-object.R
+ca068140f0dae8f7b08366b5796b9c3f *tests/testthat/test_stub.R
+710dff0033da00ba9514450a0c4f7445 *vignettes/mocks-and-testthat.Rmd
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..273db99
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,13 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method(length,mock)
+export(expect_args)
+export(expect_call)
+export(expect_called)
+export(mock)
+export(mock_args)
+export(mock_calls)
+export(stub)
+importFrom(testthat,expect)
+importFrom(testthat,expect_equal)
+importFrom(testthat,fail)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..223c4ba
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,12 @@
+v 0.4.1
+
+Fix bug whereby functions that begin with `.` don't have things mocked out in
+them.
+
+v 0.4.0
+
+Add support for stubbing depth greater than 1.
+
+Add support for nested R6 classes.
+
+
diff --git a/R/expectations.R b/R/expectations.R
new file mode 100644
index 0000000..ae0ef08
--- /dev/null
+++ b/R/expectations.R
@@ -0,0 +1,130 @@
+
+#' Expectation: does the given call match the expected?
+#'
+#' Together with \code{\link{mock}} can be used to verify whether the
+#' call expression (\code{\link{expect_call}}) and/or argument values
+#' (\code{\link{expect_args}}) match the expected.
+#'
+#' With \code{expect_called} you can check how many times has the mock
+#' object been called.
+#'
+#' @param mock_object A \code{\link{mock}} object.
+#' @param n Call number or total number of calls.
+#' @param expected_call Expected call expression; will be compared unevaluated.
+#' @param ... Arguments as passed in a call.
+#'
+#' @examples
+#' 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))
+#'
+#' @name call-expectations
+#'
+NULL
+
+
+
+#' @export
+#' @rdname call-expectations
+#'
+#' @importFrom testthat expect
+expect_call <- function (mock_object, n, expected_call) {
+ stopifnot(is_mock(mock_object))
+
+ expect(
+ 0 < n && n <= length(mock_object),
+ sprintf("call number %s not found in mock object", toString(n))
+ )
+
+ expected_call <- substitute(expected_call)
+ mocked_call <- mock_calls(mock_object)[[n]]
+
+ format_call <- function (x) paste(trimws(deparse(x)), collapse = ' ')
+
+ expect(
+ identical(mocked_call, expected_call),
+ sprintf("expected call %s does not mach actual call %s.",
+ format_call(expected_call), format_call(mocked_call))
+ )
+
+ invisible(TRUE)
+}
+
+
+#' @export
+#' @rdname call-expectations
+#'
+#' @importFrom testthat expect expect_equal
+expect_args <- function (mock_object, n, ...)
+{
+ stopifnot(is_mock(mock_object))
+
+ expect(
+ 0 < n && n <= length(mock_object),
+ sprintf("arguments list number %s not found in mock object", toString(n))
+ )
+
+ expected_args <- list(...)
+ actual_args <- mock_args(mock_object)[[n]]
+
+ expect_equal(length(actual_args), length(expected_args),
+ info = 'number of expected args does not match the actual')
+
+ for (i in seq_along(actual_args)) {
+ expect_equal(
+ actual_args[[i]],
+ expected_args[[i]],
+ label = paste(ordinal(i), 'actual argument'),
+ expected.label = paste(ordinal(i), 'expected argument')
+ )
+ }
+
+ invisible(TRUE)
+}
+
+
+ordinal <- function (x)
+{
+ stopifnot(is.integer(x), x > 0, length(x) == 1)
+ if (x %in% 11:13)
+ return(paste0(x, 'th'))
+
+ as_string <- as.character(x)
+ last_digit <- substring(as_string, nchar(as_string))
+ suffix <- switch(last_digit,
+ `1` = 'st',
+ `2` = 'nd',
+ `3` = 'rd',
+ 'th')
+ paste0(as_string, suffix)
+}
+
+
+#' @export
+#' @rdname call-expectations
+expect_called <- function (mock_object, n)
+{
+ stopifnot(is_mock(mock_object))
+
+ expect(
+ length(mock_object) == n,
+ sprintf("mock object has not been called %s time%s", toString(n),
+ (if(n>1) "s" else ""))
+ )
+}
diff --git a/R/mock-object.R b/R/mock-object.R
new file mode 100644
index 0000000..ffe6e09
--- /dev/null
+++ b/R/mock-object.R
@@ -0,0 +1,146 @@
+
+#' Create and query a mocked function.
+#'
+#' Mock object's primary use is to record calls that are made on the
+#' mocked function.
+#'
+#' Optionally values/expressions can be passed via \code{...} for the
+#' mock object to return them upon subsequent calls. Expressions are
+#' evaluated in environment \code{envir} before being returned. If no
+#' value is passed in \code{...} then \code{NULL} is returned.
+#'
+#' Passing an expression or a function call via \code{...} 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.
+#'
+#' \code{mock_calls} and \code{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.
+#'
+#'
+#' @param ... Values returned upon subsequent calls.
+#' @param cycle Whether to cycle over the return values. If \code{FALSE},
+#' will fail if called too many times.
+#' @param envir Where to evaluate the expressions being returned.
+#' @param m A \code{\link{mock}}ed function.
+#' @param x A \code{\link{mock}}ed function.
+#'
+#' @return \code{mock()} returns a mocked function which can be then used
+#' with \code{\link{with_mock}}.
+#' @return \code{mock_args()} returns a \code{list} of \code{list}s
+#' of argument values.
+#' @return \code{mock_calls()} returns a \code{list} of \code{call}s.
+#' @return \code{length.mock()} returns the number of calls invoked on \code{m}.
+#'
+#' @examples
+#' 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))
+#'
+#'
+#' @name mock
+NULL
+
+
+#' @export
+#' @rdname mock
+#'
+#' @importFrom testthat fail
+mock <- function (..., cycle = FALSE, envir = parent.frame()) {
+ stopifnot(is.environment(envir))
+
+ return_values <- eval(substitute(alist(...)))
+ return_values_env <- envir
+ call_no <- 0
+ calls <- list()
+ args <- list()
+
+ mock_impl <- function(...) {
+ call_no <<- call_no + 1
+ calls[[call_no]] <<- match.call()
+
+ args[[call_no]] <<- list(...)
+
+ if (length(return_values)) {
+ if (call_no > length(return_values) && !cycle)
+ fail("too many calls to mock object and cycle set to FALSE")
+
+ value <- return_values[[(call_no - 1) %% length(return_values) + 1]]
+ return(eval(value, envir = return_values_env))
+ }
+
+ # TODO maybe it should the mock object itself?
+ invisible(NULL)
+ }
+
+ class(mock_impl) <- 'mock'
+ mock_impl
+}
+
+
+#' @export
+#' @rdname mock
+mock_args <- function (m) {
+ stopifnot(is_mock(m))
+ environment(m)$args
+}
+
+
+#' @export
+#' @rdname mock
+mock_calls <- function (m) {
+ stopifnot(is_mock(m))
+ environment(m)$calls
+}
+
+
+is_mock <- function (object) inherits(object, 'mock')
+
+
+#' @export
+#' @rdname mock
+length.mock <- function (x)
+{
+ length(environment(x)$calls)
+}
+
diff --git a/R/mockery.R b/R/mockery.R
new file mode 100644
index 0000000..e19f5e3
--- /dev/null
+++ b/R/mockery.R
@@ -0,0 +1,34 @@
+#' R package to make mocking easier
+#'
+#' 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.
+#'
+#' @docType package
+#' @name mockery
+#'
+#' @examples
+#' 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
+#'
+#' \dontrun{
+#' 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'))
+#' })
+#' }
+NULL
\ No newline at end of file
diff --git a/R/stub.R b/R/stub.R
new file mode 100644
index 0000000..c9319e8
--- /dev/null
+++ b/R/stub.R
@@ -0,0 +1,169 @@
+
+#' Replace a function with a stub.
+#'
+#' The result of calling \code{stub} is that, when \code{where}
+#' is invoked and when it internally makes a call to \code{what},
+#' \code{how} is going to be called instead.
+#'
+#' This is much more limited in scope in comparison to
+#' \code{\link[testthat]{with_mock}} which effectively replaces
+#' \code{what} everywhere. In other words, when using \code{with_mock}
+#' and regardless of the number of intermediate calls, \code{how} is
+#' always called instead of \code{what}. However, using this API,
+#' the replacement takes place only for a single function \code{where}
+#' and only for calls originating in that function.
+#'
+#'
+#' @name stub
+#' @rdname stub
+NULL
+
+# \code{remote_stub} reverses the effect of \code{stub}.
+
+
+#' @param where Function to be called that will in turn call
+#' \code{what}.
+#' @param what Name of the function you want to stub out (a
+#' \code{character} string).
+#' @param how Replacement function (also a \code{\link{mock}} function)
+#' or a return value for which a function will be created
+#' automatically.
+#' @param depth Specifies the depth to which the function should be stubbed
+#'
+#' @export
+#' @rdname stub
+#'
+#' @examples
+#' f <- function() TRUE
+#' g <- function() f()
+#' stub(g, 'f', FALSE)
+#'
+#' # now g() returns FALSE because f() has been stubbed out
+#' g()
+#'
+`stub` <- function (where, what, how, depth=1)
+{
+ # `where` needs to be a function
+ where_name <- deparse(substitute(where))
+
+ # `what` needs to be a character value
+ stopifnot(is.character(what), length(what) == 1)
+
+ test_env <- parent.frame()
+ tree <- build_function_tree(test_env, where, where_name, depth)
+
+ mock_through_tree(tree, what, how)
+}
+
+mock_through_tree <- function(tree, what, how) {
+ for (d in tree) {
+ for (parent in d) {
+ parent_env = parent[['parent_env']]
+ func_dict = parent[['funcs']]
+ for (func_name in ls(func_dict, all.names=TRUE)) {
+ func = func_dict[[func_name]]
+ func_env = new.env(parent = environment(func))
+
+ what <- override_seperators(what, func_env)
+ where_name <- override_seperators(func_name, parent_env)
+
+ if (!is.function(how)) {
+ assign(what, function(...) how, func_env)
+ } else {
+ assign(what, how, func_env)
+ }
+
+ environment(func) <- func_env
+ assign(where_name, func, parent_env)
+ }
+ }
+ }
+}
+
+override_seperators = function(name, env) {
+ for (sep in c('::', "\\$")) {
+ if (grepl(sep, name)) {
+ elements <- strsplit(name, sep)
+ mangled_name <- paste(elements[[1]][1], elements[[1]][2], sep='XXX')
+
+ if (sep == '\\$') {
+ sep <- '$'
+ }
+
+ stub_list <- c(mangled_name)
+ if ("stub_list" %in% names(attributes(get(sep, env)))) {
+ stub_list <- c(stub_list, attributes(get(sep, env))[['stub_list']])
+ }
+
+ create_new_name <- create_create_new_name_function(stub_list, env, sep)
+ assign(sep, create_new_name, env)
+ }
+ }
+ return(if (exists('mangled_name')) mangled_name else name)
+}
+
+create_create_new_name_function <- function(stub_list, env, sep)
+{
+ force(stub_list)
+ force(env)
+ force(sep)
+
+ create_new_name <- function(pkg, func)
+ {
+ pkg_name <- deparse(substitute(pkg))
+ func_name <- deparse(substitute(func))
+ for(stub in stub_list) {
+ if (paste(pkg_name, func_name, sep='XXX') == stub) {
+ return(eval(parse(text = stub), env))
+ }
+ }
+
+ # used to avoid recursively calling the replacement function
+ eval_env = new.env(parent=parent.frame())
+ assign(sep, eval(parse(text=paste0('`', sep, '`'))), eval_env)
+
+ code = paste(pkg_name, func_name, sep=sep)
+ return(eval(parse(text=code), eval_env))
+ }
+ attributes(create_new_name) <- list(stub_list=stub_list)
+ return(create_new_name)
+}
+
+build_function_tree <- function(test_env, where, where_name, depth)
+{
+ func_dict = new.env()
+ func_dict[[where_name]] = where
+ tree = list(
+ # one depth
+ list(
+ # one parent
+ list(parent_env=test_env, funcs=func_dict)
+ )
+ )
+
+ if (depth > 1) {
+ for (d in 2:depth) {
+ num_parents = 0
+ new_depth = list()
+ for (funcs in tree[[d - 1]]) {
+ parent_dict = funcs[['funcs']]
+ for (parent_name in ls(parent_dict, all.names=TRUE)) {
+ func_dict = new.env()
+ parent_env = environment(get(parent_name, parent_dict))
+ for (func_name in ls(parent_env, all.names=TRUE)) {
+ func = get(func_name, parent_env)
+ if (is.function(func)) {
+ func_dict[[func_name]] = func
+ }
+ }
+
+ new_parent = list(parent_env=parent_env, funcs=func_dict)
+ num_parents = num_parents + 1
+ new_depth[[num_parents]] = new_parent
+ }
+ }
+ tree[[d]] = new_depth
+ }
+ }
+ return(tree)
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d1e70c9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,153 @@
+# mockery
+[![Travis-CI Build Status](https://travis-ci.org/n-s-f/mockery.svg?branch=master)](https://travis-ci.org/n-s-f/mockery)
+[![Coverage Status](https://img.shields.io/codecov/c/github/n-s-f/mockery/master.svg)](https://codecov.io/github/n-s-f/mockery?branch=master)
+[![CRAN version](http://www.r-pkg.org/badges/version/mockery)](https://cran.r-project.org/package=mockery)
+
+A mocking library for R.
+
+### Installation
+
+To install the latest CRAN release:
+
+```.R
+> install.packages('mockery')
+```
+
+To install directly from the source code in this github repository:
+
+```.R
+> # If you don't have devtools installed yet:
+> install.packages('devtools')
+>
+> # Then:
+> library('devtools')
+> devtools::install_github('n-s-f/mockery')
+```
+
+### Testing
+
+Mockery provides the capacity for stubbing out functions and for verifying
+function calls during testing.
+
+#### Stubbing
+
+Mockery's `stub` function will let you stub out a function with another
+function or simply a return value. Note that if you choose to replace the
+function with another function, the signatures of the two functions should be
+compatible.
+
+##### Examples
+
+```.R
+g = function(y) y
+f = function(x) g(x) + 1
+test_that('demonstrate stubbing', {
+ # replaces 'g' with a function that always returns 100
+ # but only when called from f
+ stub(f, 'g', 100)
+
+ # this can also be written
+ stub(f, 'g', function(...) 100)
+ expect_equal(f(1), 101)
+})
+```
+
+Stubbing works with classes of all descriptions and namespaced functions:
+
+```.R
+# this stubs the 'request_perform' function, but only
+# for httr::get, and only when it is called from within this
+# test function
+stub(httr::GET, 'request_perform', 'some html')
+
+# it is also possible to stub out a namespaced function call
+stub(some_function, 'namespace::function', 'some return value')
+```
+
+This also works with R6 classes and methods.
+
+###### Depth
+
+It's possible to specify the depth of stubbing. This is useful if you
+want to stub a function that isn't called directly by the function you call in
+your test, but is instead called by a function that that function calls.
+
+In the example below, the function `g` is both called directly from `r`, which
+we call from the test, and from `f`, which `r` calls. By specifying a depth of
+2, we tell mockery to stub `g` in both places.
+
+```.R
+g = function(y) y
+f = function(x) g(x) + 1
+r = function(x) g(x) + f(x)
+test_that('demonstrate stubbing', {
+ stub(r, 'g', 100, depth=2)
+ expect_equal(r(1), 201)
+})
+```
+
+For more examples, please see the test code contained in this repository.
+
+##### Comparison to with_mock
+
+Mockery's `stub` function has similar functionality to testthat's `with_mock`.
+
+There are several use cases in which mockery's `stub` function will work, but
+testthat's `with_mock` will not.
+
+First, unlike `with_mock`, it seamlessly allows for mocking out primitives.
+
+Second, it is easy to stub out functions from base R packages with mockery's `stub`.
+Because of how `with_mock` works, you can get into trouble if you mock such functions
+that the JIT compiler might try to use. These kinds of problems are avoided by `stub`'s
+design. As of version 2.0.0 of testthat, it will be impossible to mock functions from
+base R packages `with_mock`.
+
+The functionality of `stub` is just slightly different than that of `with_mock`. Instead
+of mocking out the object of interest for the duration of some code block, it mocks it
+out only when it is called from a specified function.
+
+#### Mocking
+
+Mock objects allow you to specify the behavior of the function you've stubbed
+out while also verifying how that function was used.
+
+```.R
+g = function(y) y
+f = function(x) g(x) + 1
+test_that('demonstrate mock object usage', {
+ # mocks can specify behavior
+ mock = mock(100)
+ stub(f, 'g', mock)
+ result = g(5)
+ expect_equal(result, 101)
+
+ # and allow you to make assertions on the mock was treated
+ expect_called(mock, 1)
+ expect_args(mock, 5)
+})
+```
+
+You can also specify multiple return values
+
+```.R
+mock = mock(1, "a", sqrt(3))
+```
+
+and access the arguments with which it was called.
+
+```.R
+mock <- mock()
+mock(x = 1)
+mock(y = 2)
+
+expect_equal(length(mock), 2)
+args <- mock_args(mock)
+
+expect_equal(args[[1]], list(x = 1))
+expect_equal(args[[2]], list(y = 2))
+```
+
+---
+
+Please report bugs and feature requests through github issues.
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..671f18b
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/inst/doc/mocks-and-testthat.R b/inst/doc/mocks-and-testthat.R
new file mode 100644
index 0000000..e04e3d6
--- /dev/null
+++ b/inst/doc/mocks-and-testthat.R
@@ -0,0 +1,126 @@
+## ----include=FALSE-------------------------------------------------------
+library(mockery)
+
+library(knitr)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+
+## ----create_mock---------------------------------------------------------
+m <- mock()
+
+## ----return_values-------------------------------------------------------
+m <- mock(1, 2, 3)
+m()
+m()
+m()
+
+## ----return_expression---------------------------------------------------
+x <- 1
+y <- 2
+m <- mock(x + y)
+m()
+
+## ----cycle_no, eval=FALSE------------------------------------------------
+# m <- mock(1, 2)
+# m()
+# #> [1] 1
+# m()
+# #> [1] 2
+# m()
+# #> Error: too many calls to mock object and cycle set to FALSE
+
+## ----cycle_true----------------------------------------------------------
+m <- mock(1, 2, cycle = TRUE)
+m()
+m()
+m()
+m()
+
+## ----cycle_expression----------------------------------------------------
+x <- 1
+y <- 2
+m <- mock(1, x + y, cycle = TRUE)
+
+m()
+m()
+
+## ----cycle_expression_2nd------------------------------------------------
+y <- 10
+m()
+m()
+
+## ----return_expression_env-----------------------------------------------
+x <- 1
+y <- 2
+e <- new.env()
+m <- mock(x + y, envir = e, cycle = TRUE)
+
+m()
+e$x <- 10
+m()
+
+## ----with_mock, message=FALSE--------------------------------------------
+library(testthat)
+
+m <- mock(1)
+f <- function (x) summary(x)
+with_mock(f = m, {
+ expect_equal(f(iris), 1)
+})
+
+## ----expect_called-------------------------------------------------------
+m <- mock(1, 2)
+
+m()
+expect_called(m, 1)
+
+m()
+expect_called(m, 2)
+
+## ----expect_called_error, eval=FALSE-------------------------------------
+# expect_called(m, 1)
+# #> Error: mock object has not been called 1 time.
+# expect_called(m, 3)
+# #> Error: mock object has not been called 3 times.
+
+## ----expect_call---------------------------------------------------------
+m <- mock(1)
+with_mock(summary = m, {
+ summary(iris)
+})
+
+expect_call(m, 1, summary(iris))
+
+## ----call_doesnt_match, eval=FALSE---------------------------------------
+# expect_call(m, 1, summary(x))
+# #> Error: expected call summary(x) does not mach actual call summary(iris).
+
+## ----expect_args---------------------------------------------------------
+expect_args(m, 1, iris)
+
+## ----expect_args_different, eval=FALSE-----------------------------------
+# expect_args(m, 1, iris[-1, ])
+# #> Error: arguments to call #1 not equal to expected arguments.
+# #> Component 1: Attributes: < Component "row.names": Numeric: lengths (150, 149) differ >
+# #> Component 1: Component 1: Numeric: lengths (150, 149) differ
+# #> Component 1: Component 2: Numeric: lengths (150, 149) differ
+# #> Component 1: Component 3: Numeric: lengths (150, 149) differ
+# #> Component 1: Component 4: Numeric: lengths (150, 149) differ
+# #> Component 1: Component 5: Lengths: 150, 149
+# #> Component 1: Component 5: Lengths (150, 149) differ (string compare on first 149)
+# #> Component 1: Component 5: 2 string mismatches
+# #> expected argument list does not mach actual one.
+
+## ----expect_args_named---------------------------------------------------
+m <- mock(1)
+with_mock(summary = m, {
+ summary(object = iris)
+})
+
+expect_args(m, 1, object = iris)
+
+## ----expect_args_unnamed, eval=FALSE-------------------------------------
+# expect_args(m, 1, iris)
+# #> Error: arguments to call #1 not equal to expected arguments.
+# #> names for target but not for current
+# #> expected argument list does not mach actual one.
+
diff --git a/inst/doc/mocks-and-testthat.Rmd b/inst/doc/mocks-and-testthat.Rmd
new file mode 100644
index 0000000..7609b58
--- /dev/null
+++ b/inst/doc/mocks-and-testthat.Rmd
@@ -0,0 +1,252 @@
+---
+title: 'Mocks: Integrating with `testthat`'
+author: "Lukasz A. Bartnik"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{Mocks: Integrating with testthat}
+ %\VignetteEngine{knitr::rmarkdown}
+ %\VignetteEncoding{UTF-8}
+---
+
+```{r include=FALSE}
+library(mockery)
+
+library(knitr)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+```
+
+
+Mock object, which is a part of the `mockery` package, started as an
+extension to [testthat](github.com/hadley/testthat)'s `with_mock()`
+facility. Its main purpose was to simplify the replacement (mocking)
+of a given function by means of `with_mock` and the later
+verification of actual calls invoked on the replacing function.
+
+The `mockery` package which provides its own stubbing facility, the
+`stub()` function. Here, however, we will look only at how `mock()`
+can be used together with `with_mock()`.
+
+
+## Mocks
+
+### Creating a `mock` function
+
+Mocking is a well-known technique when it comes to unit-testing and in
+most languages there is some notion of a mock object. In R, however, the
+natural equivalent of a mock object is a mock _function_ - and this is
+exactly what a call to `mock()` will produce.
+
+```{r create_mock}
+m <- mock()
+```
+
+
+### Return values
+
+Let's look at arguments accepted by the `mock()` _factory_ function. The
+main is a list of values which will be returned upon subsequent calls to
+`m`.
+
+```{r return_values}
+m <- mock(1, 2, 3)
+m()
+m()
+m()
+```
+
+`mock()` can take also an expression which will be evaluated upon a call.
+
+```{r return_expression}
+x <- 1
+y <- 2
+m <- mock(x + y)
+m()
+```
+
+
+### Cycling through return values
+
+By default, if the total number of calls exceeds the number of defined
+return values, the _mock_ function will throw an exception. However, one
+can also choose to cycle through the list of retun values by setting the
+`cycle` argument of `mock()` to `TRUE`.
+
+```{r cycle_no, eval=FALSE}
+m <- mock(1, 2)
+m()
+#> [1] 1
+m()
+#> [1] 2
+m()
+#> Error: too many calls to mock object and cycle set to FALSE
+```
+
+```{r cycle_true}
+m <- mock(1, 2, cycle = TRUE)
+m()
+m()
+m()
+m()
+```
+
+
+If a return value is defined by an expression, this expression will be
+evaluated each time a cycle reaches its position.
+
+```{r cycle_expression}
+x <- 1
+y <- 2
+m <- mock(1, x + y, cycle = TRUE)
+
+m()
+m()
+```
+```{r cycle_expression_2nd}
+y <- 10
+m()
+m()
+```
+
+
+### Evaluating expression in an environment of choice
+
+Finally, one can specify the environment where the return expression is
+evaluated.
+
+```{r return_expression_env}
+x <- 1
+y <- 2
+e <- new.env()
+m <- mock(x + y, envir = e, cycle = TRUE)
+
+m()
+e$x <- 10
+m()
+```
+
+
+## Integration with `with_mock()`
+
+### Simple integration
+
+Using mock functions with `testthat`'s `with_mock()` is pretty
+straightforward.
+
+```{r with_mock, message=FALSE}
+library(testthat)
+
+m <- mock(1)
+f <- function (x) summary(x)
+with_mock(f = m, {
+ expect_equal(f(iris), 1)
+})
+```
+
+
+### Verifying the number of calls
+
+The `mockery` package comes with a few additional expectations which
+might turn out to be a very useful extension to `testthat`'s API. One
+can for example verify the number and signature of calls invoked on a
+mock function, as well as the values of arguments passed in those calls.
+
+First, let's make sure the mocked function is called exactly as many
+times as we expect. This can be done with `expect_called()`.
+
+```{r expect_called}
+m <- mock(1, 2)
+
+m()
+expect_called(m, 1)
+
+m()
+expect_called(m, 2)
+```
+
+And here is what happens when we get the number of calls wrong.
+
+```{r expect_called_error, eval=FALSE}
+expect_called(m, 1)
+#> Error: mock object has not been called 1 time.
+expect_called(m, 3)
+#> Error: mock object has not been called 3 times.
+```
+
+### Verify the call signature
+
+Another new expectation is `expect_call()` which compares the signature
+of the actual call as invoked on the mock function with the expected one.
+It takes as arguments: the mock function, the call number, expected call.
+
+```{r expect_call}
+m <- mock(1)
+with_mock(summary = m, {
+ summary(iris)
+})
+
+expect_call(m, 1, summary(iris))
+```
+
+And here is what happens if the call doesn't match.
+
+```{r call_doesnt_match, eval=FALSE}
+expect_call(m, 1, summary(x))
+#> Error: expected call summary(x) does not mach actual call summary(iris).
+```
+
+### Verify values of argument
+
+Finally, one can verify whether the actual values of arguments passed
+to the mock function match the expectation. Following the previous
+example of `summary(iris)` we can make sure that the `object` parameter
+passed to `m()` was actually the `iris` dataset.
+
+```{r expect_args}
+expect_args(m, 1, iris)
+```
+
+Here is what happens if the value turns out to be different.
+
+```{r expect_args_different, eval=FALSE}
+expect_args(m, 1, iris[-1, ])
+#> Error: arguments to call #1 not equal to expected arguments.
+#> Component 1: Attributes: < Component "row.names": Numeric: lengths (150, 149) differ >
+#> Component 1: Component 1: Numeric: lengths (150, 149) differ
+#> Component 1: Component 2: Numeric: lengths (150, 149) differ
+#> Component 1: Component 3: Numeric: lengths (150, 149) differ
+#> Component 1: Component 4: Numeric: lengths (150, 149) differ
+#> Component 1: Component 5: Lengths: 150, 149
+#> Component 1: Component 5: Lengths (150, 149) differ (string compare on first 149)
+#> Component 1: Component 5: 2 string mismatches
+#> expected argument list does not mach actual one.
+```
+
+If the call has been made with an explicit argument name the same has
+to appear in `expect_args()`.
+
+```{r expect_args_named}
+m <- mock(1)
+with_mock(summary = m, {
+ summary(object = iris)
+})
+
+expect_args(m, 1, object = iris)
+```
+
+Omitting the name results in an error.
+
+```{r expect_args_unnamed, eval=FALSE}
+expect_args(m, 1, iris)
+#> Error: arguments to call #1 not equal to expected arguments.
+#> names for target but not for current
+#> expected argument list does not mach actual one.
+```
+
+
+## Further reading
+
+More information can be found in examples presented in manual pages
+for `?mock` and `?expect_call`. Extensive information about testing
+in R can be found in the documentation for the
+[testthat](github.com/hadley/testthat) package.
diff --git a/inst/doc/mocks-and-testthat.html b/inst/doc/mocks-and-testthat.html
new file mode 100644
index 0000000..ec87bdd
--- /dev/null
+++ b/inst/doc/mocks-and-testthat.html
@@ -0,0 +1,434 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+<title>Mocks</title>
+
+<script type="text/javascript">
+window.onload = function() {
+ var imgs = document.getElementsByTagName('img'), i, img;
+ for (i = 0; i < imgs.length; i++) {
+ img = imgs[i];
+ // center an image if it is the only element of its parent
+ if (img.parentElement.childElementCount === 1)
+ img.parentElement.style.textAlign = 'center';
+ }
+};
+</script>
+
+<!-- Styles for R syntax highlighter -->
+<style type="text/css">
+ pre .operator,
+ pre .paren {
+ color: rgb(104, 118, 135)
+ }
+
+ pre .literal {
+ color: #990073
+ }
+
+ pre .number {
+ color: #099;
+ }
+
+ pre .comment {
+ color: #998;
+ font-style: italic
+ }
+
+ pre .keyword {
+ color: #900;
+ font-weight: bold
+ }
+
+ pre .identifier {
+ color: rgb(0, 0, 0);
+ }
+
+ pre .string {
+ color: #d14;
+ }
+</style>
+
+<!-- R syntax highlighter -->
+<script type="text/javascript">
+var hljs=new function(){function m(p){return p.replace(/&/gm,"&").replace(/</gm,"<")}function f(r,q,p){return RegExp(q,"m"+(r.cI?"i":"")+(p?"g":""))}function b(r){for(var p=0;p<r.childNodes.length;p++){var q=r.childNodes[p];if(q.nodeName=="CODE"){return q}if(!(q.nodeType==3&&q.nodeValue.match(/\s+/))){break}}}function h(t,s){var p="";for(var r=0;r<t.childNodes.length;r++){if(t.childNodes[r].nodeType==3){var q=t.childNodes[r].nodeValue;if(s){q=q.replace(/\n/g,"")}p+=q}else{if(t.chi [...]
+hljs.initHighlightingOnLoad();
+</script>
+
+
+
+<style type="text/css">
+body, td {
+ font-family: sans-serif;
+ background-color: white;
+ font-size: 13px;
+}
+
+body {
+ max-width: 800px;
+ margin: auto;
+ padding: 1em;
+ line-height: 20px;
+}
+
+tt, code, pre {
+ font-family: 'DejaVu Sans Mono', 'Droid Sans Mono', 'Lucida Console', Consolas, Monaco, monospace;
+}
+
+h1 {
+ font-size:2.2em;
+}
+
+h2 {
+ font-size:1.8em;
+}
+
+h3 {
+ font-size:1.4em;
+}
+
+h4 {
+ font-size:1.0em;
+}
+
+h5 {
+ font-size:0.9em;
+}
+
+h6 {
+ font-size:0.8em;
+}
+
+a:visited {
+ color: rgb(50%, 0%, 50%);
+}
+
+pre, img {
+ max-width: 100%;
+}
+pre {
+ overflow-x: auto;
+}
+pre code {
+ display: block; padding: 0.5em;
+}
+
+code {
+ font-size: 92%;
+ border: 1px solid #ccc;
+}
+
+code[class] {
+ background-color: #F8F8F8;
+}
+
+table, td, th {
+ border: none;
+}
+
+blockquote {
+ color:#666666;
+ margin:0;
+ padding-left: 1em;
+ border-left: 0.5em #EEE solid;
+}
+
+hr {
+ height: 0px;
+ border-bottom: none;
+ border-top-width: thin;
+ border-top-style: dotted;
+ border-top-color: #999999;
+}
+
+ at media print {
+ * {
+ background: transparent !important;
+ color: black !important;
+ filter:none !important;
+ -ms-filter: none !important;
+ }
+
+ body {
+ font-size:12pt;
+ max-width:100%;
+ }
+
+ a, a:visited {
+ text-decoration: underline;
+ }
+
+ hr {
+ visibility: hidden;
+ page-break-before: always;
+ }
+
+ pre, blockquote {
+ padding-right: 1em;
+ page-break-inside: avoid;
+ }
+
+ tr, img {
+ page-break-inside: avoid;
+ }
+
+ img {
+ max-width: 100% !important;
+ }
+
+ @page :left {
+ margin: 15mm 20mm 15mm 10mm;
+ }
+
+ @page :right {
+ margin: 15mm 10mm 15mm 20mm;
+ }
+
+ p, h2, h3 {
+ orphans: 3; widows: 3;
+ }
+
+ h2, h3 {
+ page-break-after: avoid;
+ }
+}
+</style>
+
+
+
+</head>
+
+<body>
+<p>Mock object, which is a part of the <code>mockery</code> package, started as an
+extension to <a href="github.com/hadley/testthat">testthat</a>'s <code>with_mock()</code>
+facility. Its main purpose was to simplify the replacement (mocking)
+of a given function by means of <code>with_mock</code> and the later
+verification of actual calls invoked on the replacing function.</p>
+
+<p>The <code>mockery</code> package which provides its own stubbing facility, the
+<code>stub()</code> function. Here, however, we will look only at how <code>mock()</code>
+can be used together with <code>with_mock()</code>.</p>
+
+<h2>Mocks</h2>
+
+<h3>Creating a <code>mock</code> function</h3>
+
+<p>Mocking is a well-known technique when it comes to unit-testing and in
+most languages there is some notion of a mock object. In R, however, the
+natural equivalent of a mock object is a mock <em>function</em> - and this is
+exactly what a call to <code>mock()</code> will produce.</p>
+
+<pre><code class="r">m <- mock()
+</code></pre>
+
+<h3>Return values</h3>
+
+<p>Let's look at arguments accepted by the <code>mock()</code> <em>factory</em> function. The
+main is a list of values which will be returned upon subsequent calls to
+<code>m</code>.</p>
+
+<pre><code class="r">m <- mock(1, 2, 3)
+m()
+#> [1] 1
+m()
+#> [1] 2
+m()
+#> [1] 3
+</code></pre>
+
+<p><code>mock()</code> can take also an expression which will be evaluated upon a call.</p>
+
+<pre><code class="r">x <- 1
+y <- 2
+m <- mock(x + y)
+m()
+#> [1] 3
+</code></pre>
+
+<h3>Cycling through return values</h3>
+
+<p>By default, if the total number of calls exceeds the number of defined
+return values, the <em>mock</em> function will throw an exception. However, one
+can also choose to cycle through the list of retun values by setting the
+<code>cycle</code> argument of <code>mock()</code> to <code>TRUE</code>.</p>
+
+<pre><code class="r">m <- mock(1, 2)
+m()
+#> [1] 1
+m()
+#> [1] 2
+m()
+#> Error: too many calls to mock object and cycle set to FALSE
+</code></pre>
+
+<pre><code class="r">m <- mock(1, 2, cycle = TRUE)
+m()
+#> [1] 1
+m()
+#> [1] 2
+m()
+#> [1] 1
+m()
+#> [1] 2
+</code></pre>
+
+<p>If a return value is defined by an expression, this expression will be
+evaluated each time a cycle reaches its position.</p>
+
+<pre><code class="r">x <- 1
+y <- 2
+m <- mock(1, x + y, cycle = TRUE)
+
+m()
+#> [1] 1
+m()
+#> [1] 3
+</code></pre>
+
+<pre><code class="r">y <- 10
+m()
+#> [1] 1
+m()
+#> [1] 11
+</code></pre>
+
+<h3>Evaluating expression in an environment of choice</h3>
+
+<p>Finally, one can specify the environment where the return expression is
+evaluated.</p>
+
+<pre><code class="r">x <- 1
+y <- 2
+e <- new.env()
+m <- mock(x + y, envir = e, cycle = TRUE)
+
+m()
+#> [1] 3
+e$x <- 10
+m()
+#> [1] 12
+</code></pre>
+
+<h2>Integration with <code>with_mock()</code></h2>
+
+<h3>Simple integration</h3>
+
+<p>Using mock functions with <code>testthat</code>'s <code>with_mock()</code> is pretty
+straightforward.</p>
+
+<pre><code class="r">library(testthat)
+
+m <- mock(1)
+f <- function (x) summary(x)
+with_mock(f = m, {
+ expect_equal(f(iris), 1)
+})
+</code></pre>
+
+<h3>Verifying the number of calls</h3>
+
+<p>The <code>mockery</code> package comes with a few additional expectations which
+might turn out to be a very useful extension to <code>testthat</code>'s API. One
+can for example verify the number and signature of calls invoked on a
+mock function, as well as the values of arguments passed in those calls.</p>
+
+<p>First, let's make sure the mocked function is called exactly as many
+times as we expect. This can be done with <code>expect_called()</code>.</p>
+
+<pre><code class="r">m <- mock(1, 2)
+
+m()
+#> [1] 1
+expect_called(m, 1)
+
+m()
+#> [1] 2
+expect_called(m, 2)
+</code></pre>
+
+<p>And here is what happens when we get the number of calls wrong.</p>
+
+<pre><code class="r">expect_called(m, 1)
+#> Error: mock object has not been called 1 time.
+expect_called(m, 3)
+#> Error: mock object has not been called 3 times.
+</code></pre>
+
+<h3>Verify the call signature</h3>
+
+<p>Another new expectation is <code>expect_call()</code> which compares the signature
+of the actual call as invoked on the mock function with the expected one.
+It takes as arguments: the mock function, the call number, expected call.</p>
+
+<pre><code class="r">m <- mock(1)
+with_mock(summary = m, {
+ summary(iris)
+})
+#> [1] 1
+
+expect_call(m, 1, summary(iris))
+</code></pre>
+
+<p>And here is what happens if the call doesn't match.</p>
+
+<pre><code class="r">expect_call(m, 1, summary(x))
+#> Error: expected call summary(x) does not mach actual call summary(iris).
+</code></pre>
+
+<h3>Verify values of argument</h3>
+
+<p>Finally, one can verify whether the actual values of arguments passed
+to the mock function match the expectation. Following the previous
+example of <code>summary(iris)</code> we can make sure that the <code>object</code> parameter
+passed to <code>m()</code> was actually the <code>iris</code> dataset.</p>
+
+<pre><code class="r">expect_args(m, 1, iris)
+</code></pre>
+
+<p>Here is what happens if the value turns out to be different.</p>
+
+<pre><code class="r">expect_args(m, 1, iris[-1, ])
+#> Error: arguments to call #1 not equal to expected arguments.
+#> Component 1: Attributes: < Component "row.names": Numeric: lengths (150, 149) differ >
+#> Component 1: Component 1: Numeric: lengths (150, 149) differ
+#> Component 1: Component 2: Numeric: lengths (150, 149) differ
+#> Component 1: Component 3: Numeric: lengths (150, 149) differ
+#> Component 1: Component 4: Numeric: lengths (150, 149) differ
+#> Component 1: Component 5: Lengths: 150, 149
+#> Component 1: Component 5: Lengths (150, 149) differ (string compare on first 149)
+#> Component 1: Component 5: 2 string mismatches
+#> expected argument list does not mach actual one.
+</code></pre>
+
+<p>If the call has been made with an explicit argument name the same has
+to appear in <code>expect_args()</code>.</p>
+
+<pre><code class="r">m <- mock(1)
+with_mock(summary = m, {
+ summary(object = iris)
+})
+#> [1] 1
+
+expect_args(m, 1, object = iris)
+</code></pre>
+
+<p>Omitting the name results in an error.</p>
+
+<pre><code class="r">expect_args(m, 1, iris)
+#> Error: arguments to call #1 not equal to expected arguments.
+#> names for target but not for current
+#> expected argument list does not mach actual one.
+</code></pre>
+
+<h2>Further reading</h2>
+
+<p>More information can be found in examples presented in manual pages
+for <code>?mock</code> and <code>?expect_call</code>. Extensive information about testing
+in R can be found in the documentation for the
+<a href="github.com/hadley/testthat">testthat</a> package.</p>
+
+</body>
+
+</html>
diff --git a/man/call-expectations.Rd b/man/call-expectations.Rd
new file mode 100644
index 0000000..c1176a8
--- /dev/null
+++ b/man/call-expectations.Rd
@@ -0,0 +1,55 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/expectations.R
+\name{call-expectations}
+\alias{call-expectations}
+\alias{expect_call}
+\alias{expect_args}
+\alias{expect_called}
+\title{Expectation: does the given call match the expected?}
+\usage{
+expect_call(mock_object, n, expected_call)
+
+expect_args(mock_object, n, ...)
+
+expect_called(mock_object, n)
+}
+\arguments{
+\item{mock_object}{A \code{\link{mock}} object.}
+
+\item{n}{Call number or total number of calls.}
+
+\item{expected_call}{Expected call expression; will be compared unevaluated.}
+
+\item{...}{Arguments as passed in a call.}
+}
+\description{
+Together with \code{\link{mock}} can be used to verify whether the
+call expression (\code{\link{expect_call}}) and/or argument values
+(\code{\link{expect_args}}) match the expected.
+}
+\details{
+With \code{expect_called} you can check how many times has the mock
+object been called.
+}
+\examples{
+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))
+
+}
diff --git a/man/mock.Rd b/man/mock.Rd
new file mode 100644
index 0000000..b8d8013
--- /dev/null
+++ b/man/mock.Rd
@@ -0,0 +1,106 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/mock-object.R
+\name{mock}
+\alias{mock}
+\alias{mock}
+\alias{mock_args}
+\alias{mock_calls}
+\alias{length.mock}
+\title{Create and query a mocked function.}
+\usage{
+mock(..., cycle = FALSE, envir = parent.frame())
+
+mock_args(m)
+
+mock_calls(m)
+
+\method{length}{mock}(x)
+}
+\arguments{
+\item{...}{Values returned upon subsequent calls.}
+
+\item{cycle}{Whether to cycle over the return values. If \code{FALSE},
+will fail if called too many times.}
+
+\item{envir}{Where to evaluate the expressions being returned.}
+
+\item{m}{A \code{\link{mock}}ed function.}
+
+\item{x}{A \code{\link{mock}}ed function.}
+}
+\value{
+\code{mock()} returns a mocked function which can be then used
+ with \code{\link{with_mock}}.
+
+\code{mock_args()} returns a \code{list} of \code{list}s
+ of argument values.
+
+\code{mock_calls()} returns a \code{list} of \code{call}s.
+
+\code{length.mock()} returns the number of calls invoked on \code{m}.
+}
+\description{
+Mock object's primary use is to record calls that are made on the
+mocked function.
+}
+\details{
+Optionally values/expressions can be passed via \code{...} for the
+mock object to return them upon subsequent calls. Expressions are
+evaluated in environment \code{envir} before being returned. If no
+value is passed in \code{...} then \code{NULL} is returned.
+
+Passing an expression or a function call via \code{...} 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.
+
+\code{mock_calls} and \code{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.
+}
+\examples{
+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))
+
+
+}
diff --git a/man/mockery.Rd b/man/mockery.Rd
new file mode 100644
index 0000000..6984853
--- /dev/null
+++ b/man/mockery.Rd
@@ -0,0 +1,37 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/mockery.R
+\docType{package}
+\name{mockery}
+\alias{mockery}
+\alias{mockery-package}
+\title{R package to make mocking easier}
+\description{
+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.
+}
+\examples{
+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
+
+\dontrun{
+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'))
+})
+}
+}
diff --git a/man/stub.Rd b/man/stub.Rd
new file mode 100644
index 0000000..e599b36
--- /dev/null
+++ b/man/stub.Rd
@@ -0,0 +1,45 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/stub.R
+\name{stub}
+\alias{stub}
+\alias{stub}
+\title{Replace a function with a stub.}
+\usage{
+stub(where, what, how, depth = 1)
+}
+\arguments{
+\item{where}{Function to be called that will in turn call
+\code{what}.}
+
+\item{what}{Name of the function you want to stub out (a
+\code{character} string).}
+
+\item{how}{Replacement function (also a \code{\link{mock}} function)
+or a return value for which a function will be created
+automatically.}
+
+\item{depth}{Specifies the depth to which the function should be stubbed}
+}
+\description{
+The result of calling \code{stub} is that, when \code{where}
+is invoked and when it internally makes a call to \code{what},
+\code{how} is going to be called instead.
+}
+\details{
+This is much more limited in scope in comparison to
+\code{\link[testthat]{with_mock}} which effectively replaces
+\code{what} everywhere. In other words, when using \code{with_mock}
+and regardless of the number of intermediate calls, \code{how} is
+always called instead of \code{what}. However, using this API,
+the replacement takes place only for a single function \code{where}
+and only for calls originating in that function.
+}
+\examples{
+f <- function() TRUE
+g <- function() f()
+stub(g, 'f', FALSE)
+
+# now g() returns FALSE because f() has been stubbed out
+g()
+
+}
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..bdfcead
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(mockery)
+
+test_check("mockery")
diff --git a/tests/testthat/test-mock-object.R b/tests/testthat/test-mock-object.R
new file mode 100644
index 0000000..b66817e
--- /dev/null
+++ b/tests/testthat/test-mock-object.R
@@ -0,0 +1,179 @@
+context("Mock objects")
+
+test_that("mock single brackets", {
+ m <- mock(1)
+ m()
+ expect_equal(mock_calls(m)[[1]], bquote(m()))
+})
+
+
+test_that("mock length", {
+ m <- mock(1, cycle = TRUE)
+ expect_equal(length(m), 0)
+
+ m()
+ expect_equal(length(m), 1)
+
+ m()
+ m()
+ expect_equal(length(m), 3)
+})
+
+
+test_that("mock cyclic returns", {
+ m <- mock(1, cycle = TRUE)
+ expect_equal(lapply(1:10, m), as.list(rep(1, 10)))
+
+ m <- mock(1, 2, cycle = TRUE)
+ expect_equal(lapply(1:10, m), as.list(rep(1:2, 5)))
+
+ m <- mock(1, 2, 3, cycle = TRUE)
+ expect_equal(lapply(1:12, m), as.list(rep(1:3, 4)))
+})
+
+
+test_that("call list", {
+ m <- mock()
+ e <- environment(m)
+
+ with_mock(summary = m, {
+ summary(iris)
+ })
+
+ expect_true(exists('calls', envir = e))
+ expect_length(e$calls, 1)
+ expect_equal(e$calls[[1]], bquote(summary(iris)))
+})
+
+
+test_that("expect calls", {
+ m <- mock()
+
+ with_mock(summary = m, {
+ summary(iris)
+ })
+
+ expect_call(m, 1, summary(iris))
+})
+
+
+test_that("error for long call is formatted well", {
+ m <- mock()
+ mockery::stub(read.csv, "read.table", m)
+ read.csv('file.csv')
+
+ # test mock with mock, how crazy is that!
+ # we cannot call expect_failure because it messes up calls to expect()
+ # thus we intercept calls to expect() and later compare arguments
+ test_mock <- mock(TRUE, TRUE)
+ with_mock(expect = test_mock, {
+ expect_call(m, 1, x)
+ })
+
+ expect_called(test_mock, 2)
+
+ err <- paste0(
+ 'expected call x does not mach actual call ',
+ 'read.table(file = file, header = header, sep = sep, quote = quote, ',
+ 'dec = dec, fill = fill, comment.char = comment.char).'
+ )
+ expect_args(test_mock, 2, FALSE, err)
+})
+
+test_that("empty return list", {
+ m <- mock()
+ expect_null(m())
+})
+
+
+test_that("too many calls", {
+ m <- mock(1)
+ expect_equal(1, m())
+ expect_failure(m(), "too many calls to mock object and cycle set to FALSE")
+})
+
+
+test_that("return expression", {
+ e <- new.env(parent = globalenv())
+ m <- mock(fun_x("a"),
+ fun_x("a") == "a",
+ envir = e, cycle = TRUE)
+
+ e$fun_x <- function(x)x
+ expect_equal(m(), "a")
+ expect_true(m())
+
+ e$fun_x <- function(x)"abc"
+ expect_equal(m(), "abc")
+ expect_false(m())
+})
+
+
+test_that("operator $ works for mock", {
+ m <- mock()
+ expect_equal(mock_calls(m), list())
+ expect_equal(mock_args(m), list())
+})
+
+
+test_that("arguments are recorded", {
+ m <- mock()
+ m(x = 1)
+ m(y = 2, z = iris)
+
+ expect_equal(length(m), 2)
+ expect_named(mock_args(m)[[1]], 'x')
+ expect_named(mock_args(m)[[2]], c('y', 'z'))
+
+ expect_equal(mock_args(m)[[1]]$x, 1)
+ expect_equal(mock_args(m)[[2]]$y, 2)
+ expect_equal(mock_args(m)[[2]]$z, iris)
+})
+
+
+test_that("expect args", {
+ m <- mock()
+ m(iris)
+ m(x = 1)
+
+ # compares values, not symbols
+ y <- 2
+ z <- iris
+ m(y = y, z = z)
+
+ expect_args(m, 1, iris)
+ expect_args(m, 2, x = 1)
+ expect_args(m, 3, y = 2, z = iris)
+})
+
+
+test_that("expect args in with_mock", {
+ m <- mock()
+
+ with_mock(lm = m, {
+ x <- iris
+ lm(Sepal.Width ~ Sepal.Length, data = x)
+ })
+
+ expect_args(m, 1, Sepal.Width ~ Sepal.Length, data = iris)
+})
+
+
+test_that("calls are counted", {
+ m <- mock()
+ expect_called(m, 0)
+
+ m()
+ expect_called(m, 1)
+
+ m()
+ expect_called(m, 2)
+})
+
+
+test_that("appropriate message if counts are missing", {
+ m <- mock()
+ expect_failure(expect_called(m, 1), "mock object has not been called 1 time")
+ expect_failure(expect_called(m, 2), "mock object has not been called 2 times")
+})
+
diff --git a/tests/testthat/test_stub.R b/tests/testthat/test_stub.R
new file mode 100644
index 0000000..43275b7
--- /dev/null
+++ b/tests/testthat/test_stub.R
@@ -0,0 +1,332 @@
+testthat::context('stub')
+
+a = 10
+f = function(x) x
+g = function(x) f(x) + a
+test_that('stubs function with return value', {
+ # before stubbing
+ expect_equal(g(20), 30)
+
+ # when
+ stub(g, 'f', 100)
+
+ # then
+ expect_equal(g(20), 110)
+})
+
+test_that('values restored after test', {
+ expect_equal(f(15), 15)
+ expect_equal(g(15), 25)
+})
+
+test_that('stubs function with function', {
+ # given
+ a = 10
+ f = function(x) x
+ g = function(x) f(x) + a
+
+ # before stubbing
+ expect_equal(g(20), 30)
+
+ # when
+ stub(g, 'f', function(...) 500)
+
+ # then
+ expect_equal(g(10), 510)
+})
+
+
+test_that('stubs function from namespace', {
+ # given
+ f = function() testthat::capture_output(print('hello'))
+
+ # before stubbing
+ expect_true(grepl('hello', f()))
+
+ # when
+ stub(f, 'testthat::capture_output', 10)
+
+ # then
+ expect_equal(f(), 10)
+})
+
+test_that('does not stub other namespeaced functions', {
+ # given
+ f = function() {
+ a = testthat::capture_output(print('hello'))
+ b = testthat::is_null('not null')
+ return(c(a, b))
+ }
+
+ # when
+ stub(f, 'testthat::is_null', 'stubbed output')
+
+ # then
+ result = f()
+ expect_true(grepl('hello', result[1]))
+ expect_equal(result[2], 'stubbed output')
+})
+
+test_that('stub multiple functions', {
+ # given
+ f = function(x) x + 10
+ g = function(y) y + 20
+ h = function(z) f(z) + g(z)
+
+ # when
+ stub(h, 'f', 300)
+ stub(h, 'g', 500)
+
+ # then
+ expect_equal(h(1), 800)
+})
+
+test_that('stub multiple namespaced functions', {
+ # given
+ h = function(x) mockery::stub(x) + mockery::get_function_source(x)
+
+ # when
+ stub(h, 'mockery::stub', 300)
+ stub(h, 'mockery::get_function_source', 500)
+
+ # then
+ expect_equal(h(1), 800)
+})
+
+test_that('stub works with do.call', {
+ # given
+ f = function(x) x + 10
+ g = function(x) do.call('f', list(x))
+
+ # before stub
+ expect_equal(g(10), 20)
+
+ # stub
+ stub(g, 'f', 100)
+
+ # then
+ expect_equal(g(10), 100)
+})
+
+test_that('stub works with lapply', {
+ # given
+ f = function(x) x + 10
+ g = function(x) lapply(x, 'f')
+ l = list(1, 2, 3)
+
+ # before stub
+ expect_equal(g(l), list(11, 12, 13))
+
+ # stub
+ stub(g, 'f', 100)
+
+ # then
+ expect_equal(g(l), list(100, 100, 100))
+})
+
+test_that('stub works well with mock object', {
+ # given
+ f = function(x) x + 10
+ g = function(x) f(x)
+
+ mock_object = mock(100)
+ stub(g, 'f', mock_object)
+
+ # when
+ result = g(5)
+
+ # then
+ expect_equal(result, 100)
+})
+
+f = function(x) x + 10
+g = function(x) f(x)
+test_that('mock object returns value', {
+ mock_object = mock(1)
+ stub(g, 'f', mock_object)
+
+ expect_equal(g('anything'), 1)
+ expect_called(mock_object, 1)
+ expect_args(mock_object, 1, 'anything')
+})
+
+test_that('mock object multiple return values', {
+ mock_object = mock(1, "a", sqrt(3))
+ stub(g, 'f', mock_object)
+
+ expect_equal(g('anything'), 1)
+ expect_equal(g('anything'), "a")
+ expect_equal(g('anything'), sqrt(3))
+})
+
+test_that('mock object accessing values of arguments', {
+ mock_object <- mock()
+ mock_object(x = 1)
+ mock_object(y = 2)
+
+ expect_equal(length(mock_object), 2)
+ args <- mock_args(mock_object)
+
+ expect_equal(args[[1]], list(x = 1))
+ expect_equal(args[[2]], list(y = 2))
+})
+
+test_that('mock object accessing call expressions', {
+ mock_object <- mock()
+ mock_object(x = 1)
+ mock_object(y = 2)
+
+ expect_equal(length(mock_object), 2)
+ calls <- mock_calls(mock_object)
+
+ expect_equal(calls[[1]], quote(mock_object(x = 1)))
+ expect_equal(calls[[2]], quote(mock_object(y = 2)))
+})
+
+library(R6)
+
+some_other_class = R6Class("some_class",
+ public = list(
+ external_method = function() {return('this is external output')}
+ )
+)
+
+some_class = R6Class("some_class",
+ public = list(
+ some_method = function() {return(some_function())},
+ some_method_prime = function() {return(some_function())},
+ other_method = function() {return('method in class')},
+ method_without_other = function() { self$other_method() },
+ method_with_other = function() {
+ other <- some_other_class$new()
+ other$external_method()
+ self$other_method()
+ }
+ )
+)
+
+# Calling function from R6 method
+ some_function = function() {return("called from within class")}
+ obj = some_class$new()
+test_that('stub works with R6 methods', {
+ stub(obj$some_method, 'some_function', 'stub has been called')
+ expect_equal(obj$some_method(), 'stub has been called')
+})
+
+test_that('stub works with R6 methods that call internal methods in them', {
+ stub(obj$method_without_other, 'self$other_method', 'stub has been called')
+ expect_equal(obj$method_without_other(), 'stub has been called')
+})
+
+test_that('stub works with R6 methods that have other objects in them', {
+ stub(obj$method_with_other, 'self$other_method', 'stub has been called')
+ expect_equal(obj$method_with_other(), 'stub has been called')
+})
+
+test_that('R6 method does not stay stubbed', {
+ expect_equal(obj$some_method(), 'called from within class')
+})
+
+# Calling R6 method from function
+other_func = function() {
+ obj = some_class$new()
+ return(obj$other_method())
+}
+test_that('stub works for stubbing R6 methods from within function calls', {
+ stub(other_func, 'obj$other_method', 'stubbed R6 method')
+ expect_equal(other_func(), 'stubbed R6 method')
+})
+
+test_that('stub does not stay in effect', {
+ expect_equal(other_func(), 'method in class')
+})
+
+test_that('stub out of namespaced functions', {
+ expect_true(grepl('hello', testthat::capture_output(print('hello'))))
+ stub(testthat::capture_output, 'paste0', 'stubbed function')
+ expect_equal(testthat::capture_output(print('hello')), 'stubbed function')
+})
+
+test_that('stub multiple namespaced and R6 functions from within test env', {
+ stub(testthat::capture_output, 'paste0', 'stub 1')
+ stub(obj$some_method, 'some_function', 'stub 2')
+ stub(obj$some_method_prime, 'some_function', 'stub 3')
+ stub(testthat::test_that, 'test_code', 'stub 4')
+
+ # all stubs are active
+ expect_equal(testthat::capture_output(print('hello')), 'stub 1')
+ expect_equal(obj$some_method(), 'stub 2')
+ expect_equal(obj$some_method_prime(), 'stub 3')
+ expect_equal(testthat::test_that('a', print), 'stub 4')
+
+ # non mocked R6 and namespaced functions work as expected
+ expect_equal(obj$other_method(), 'method in class')
+ testthat::expect_failure(expect_equal(4, 5))
+})
+
+h = function(x) 'called h'
+g = function(x) h(x)
+f = function(x) g(x)
+test_that('use can specify depth of mocking', {
+ stub_string = 'called stub!'
+ stub(f, 'h', stub_string, depth=2)
+ expect_equal(f(1), stub_string)
+})
+
+h = function(x) 'called h'
+g = function(x) h(x)
+f = function(x) paste0(h(x), g(x))
+test_that('mocked function is mocked at all depths', {
+ stub_string = 'called stub!'
+ stub(f, 'h', stub_string, depth=2)
+ expect_equal(f(1), 'called stub!called stub!')
+})
+
+h = function(x) 'called h'
+g = function(x) h(x)
+r = function(x) g(x)
+f = function(x) paste0(h(x), r(x))
+test_that('mocked function is mocked at all depths', {
+ stub_string = 'called stub!'
+ stub(f, 'h', stub_string, depth=3)
+ expect_equal(f(1), 'called stub!called stub!')
+})
+
+h = function(x) 'called h'
+t = function(x) h(x)
+g = function(x) h(x)
+r = function(x) paste0(t(x), g(x))
+u = function(x) paste0(h(x), g(x))
+
+f = function(x) paste0(h(x), r(x), u(x))
+
+t_env = new.env(parent=baseenv())
+assign('h', h, t_env)
+environment(t) = t_env
+
+u_env = new.env(parent=baseenv())
+assign('g', g, u_env)
+assign('h', h, u_env)
+environment(u) = u_env
+
+f_env = new.env(parent=baseenv())
+assign('u', u, f_env)
+assign('h', h, f_env)
+assign('r', r, f_env)
+environment(f) = f_env
+
+a = function(x) x
+environment(a) = f_env
+
+test_that('mocked function is mocked at all depths across paths', {
+ stub_string = 'called stub!'
+ stub(f, 'h', stub_string, depth=4)
+ expect_equal(f(1), 'called stub!called stub!called stub!called stub!called stub!')
+})
+
+.a = function(x) h(x)
+test_that('mocks hidden functions', {
+ stub_string = 'called stub!'
+ stub(.a, 'h', stub_string, depth=4)
+ expect_equal(f(1), 'called stub!called stub!called stub!called stub!called stub!')
+})
diff --git a/vignettes/mocks-and-testthat.Rmd b/vignettes/mocks-and-testthat.Rmd
new file mode 100644
index 0000000..7609b58
--- /dev/null
+++ b/vignettes/mocks-and-testthat.Rmd
@@ -0,0 +1,252 @@
+---
+title: 'Mocks: Integrating with `testthat`'
+author: "Lukasz A. Bartnik"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{Mocks: Integrating with testthat}
+ %\VignetteEngine{knitr::rmarkdown}
+ %\VignetteEncoding{UTF-8}
+---
+
+```{r include=FALSE}
+library(mockery)
+
+library(knitr)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+```
+
+
+Mock object, which is a part of the `mockery` package, started as an
+extension to [testthat](github.com/hadley/testthat)'s `with_mock()`
+facility. Its main purpose was to simplify the replacement (mocking)
+of a given function by means of `with_mock` and the later
+verification of actual calls invoked on the replacing function.
+
+The `mockery` package which provides its own stubbing facility, the
+`stub()` function. Here, however, we will look only at how `mock()`
+can be used together with `with_mock()`.
+
+
+## Mocks
+
+### Creating a `mock` function
+
+Mocking is a well-known technique when it comes to unit-testing and in
+most languages there is some notion of a mock object. In R, however, the
+natural equivalent of a mock object is a mock _function_ - and this is
+exactly what a call to `mock()` will produce.
+
+```{r create_mock}
+m <- mock()
+```
+
+
+### Return values
+
+Let's look at arguments accepted by the `mock()` _factory_ function. The
+main is a list of values which will be returned upon subsequent calls to
+`m`.
+
+```{r return_values}
+m <- mock(1, 2, 3)
+m()
+m()
+m()
+```
+
+`mock()` can take also an expression which will be evaluated upon a call.
+
+```{r return_expression}
+x <- 1
+y <- 2
+m <- mock(x + y)
+m()
+```
+
+
+### Cycling through return values
+
+By default, if the total number of calls exceeds the number of defined
+return values, the _mock_ function will throw an exception. However, one
+can also choose to cycle through the list of retun values by setting the
+`cycle` argument of `mock()` to `TRUE`.
+
+```{r cycle_no, eval=FALSE}
+m <- mock(1, 2)
+m()
+#> [1] 1
+m()
+#> [1] 2
+m()
+#> Error: too many calls to mock object and cycle set to FALSE
+```
+
+```{r cycle_true}
+m <- mock(1, 2, cycle = TRUE)
+m()
+m()
+m()
+m()
+```
+
+
+If a return value is defined by an expression, this expression will be
+evaluated each time a cycle reaches its position.
+
+```{r cycle_expression}
+x <- 1
+y <- 2
+m <- mock(1, x + y, cycle = TRUE)
+
+m()
+m()
+```
+```{r cycle_expression_2nd}
+y <- 10
+m()
+m()
+```
+
+
+### Evaluating expression in an environment of choice
+
+Finally, one can specify the environment where the return expression is
+evaluated.
+
+```{r return_expression_env}
+x <- 1
+y <- 2
+e <- new.env()
+m <- mock(x + y, envir = e, cycle = TRUE)
+
+m()
+e$x <- 10
+m()
+```
+
+
+## Integration with `with_mock()`
+
+### Simple integration
+
+Using mock functions with `testthat`'s `with_mock()` is pretty
+straightforward.
+
+```{r with_mock, message=FALSE}
+library(testthat)
+
+m <- mock(1)
+f <- function (x) summary(x)
+with_mock(f = m, {
+ expect_equal(f(iris), 1)
+})
+```
+
+
+### Verifying the number of calls
+
+The `mockery` package comes with a few additional expectations which
+might turn out to be a very useful extension to `testthat`'s API. One
+can for example verify the number and signature of calls invoked on a
+mock function, as well as the values of arguments passed in those calls.
+
+First, let's make sure the mocked function is called exactly as many
+times as we expect. This can be done with `expect_called()`.
+
+```{r expect_called}
+m <- mock(1, 2)
+
+m()
+expect_called(m, 1)
+
+m()
+expect_called(m, 2)
+```
+
+And here is what happens when we get the number of calls wrong.
+
+```{r expect_called_error, eval=FALSE}
+expect_called(m, 1)
+#> Error: mock object has not been called 1 time.
+expect_called(m, 3)
+#> Error: mock object has not been called 3 times.
+```
+
+### Verify the call signature
+
+Another new expectation is `expect_call()` which compares the signature
+of the actual call as invoked on the mock function with the expected one.
+It takes as arguments: the mock function, the call number, expected call.
+
+```{r expect_call}
+m <- mock(1)
+with_mock(summary = m, {
+ summary(iris)
+})
+
+expect_call(m, 1, summary(iris))
+```
+
+And here is what happens if the call doesn't match.
+
+```{r call_doesnt_match, eval=FALSE}
+expect_call(m, 1, summary(x))
+#> Error: expected call summary(x) does not mach actual call summary(iris).
+```
+
+### Verify values of argument
+
+Finally, one can verify whether the actual values of arguments passed
+to the mock function match the expectation. Following the previous
+example of `summary(iris)` we can make sure that the `object` parameter
+passed to `m()` was actually the `iris` dataset.
+
+```{r expect_args}
+expect_args(m, 1, iris)
+```
+
+Here is what happens if the value turns out to be different.
+
+```{r expect_args_different, eval=FALSE}
+expect_args(m, 1, iris[-1, ])
+#> Error: arguments to call #1 not equal to expected arguments.
+#> Component 1: Attributes: < Component "row.names": Numeric: lengths (150, 149) differ >
+#> Component 1: Component 1: Numeric: lengths (150, 149) differ
+#> Component 1: Component 2: Numeric: lengths (150, 149) differ
+#> Component 1: Component 3: Numeric: lengths (150, 149) differ
+#> Component 1: Component 4: Numeric: lengths (150, 149) differ
+#> Component 1: Component 5: Lengths: 150, 149
+#> Component 1: Component 5: Lengths (150, 149) differ (string compare on first 149)
+#> Component 1: Component 5: 2 string mismatches
+#> expected argument list does not mach actual one.
+```
+
+If the call has been made with an explicit argument name the same has
+to appear in `expect_args()`.
+
+```{r expect_args_named}
+m <- mock(1)
+with_mock(summary = m, {
+ summary(object = iris)
+})
+
+expect_args(m, 1, object = iris)
+```
+
+Omitting the name results in an error.
+
+```{r expect_args_unnamed, eval=FALSE}
+expect_args(m, 1, iris)
+#> Error: arguments to call #1 not equal to expected arguments.
+#> names for target but not for current
+#> expected argument list does not mach actual one.
+```
+
+
+## Further reading
+
+More information can be found in examples presented in manual pages
+for `?mock` and `?expect_call`. Extensive information about testing
+in R can be found in the documentation for the
+[testthat](github.com/hadley/testthat) package.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-mockery.git
More information about the debian-med-commit
mailing list