mockery/0000755000176200001440000000000013201367135011724 5ustar liggesusersmockery/inst/0000755000176200001440000000000013201337231012673 5ustar liggesusersmockery/inst/doc/0000755000176200001440000000000013201337231013440 5ustar liggesusersmockery/inst/doc/mocks-and-testthat.R0000644000176200001440000000656513201337231017311 0ustar liggesusers## ----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. mockery/inst/doc/mocks-and-testthat.html0000644000176200001440000004703613201337231020052 0ustar liggesusers Mocks

Mock object, which is a part of the mockery package, started as an extension to 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.

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.

m <- mock(1, 2, 3)
m()
#> [1] 1
m()
#> [1] 2
m()
#> [1] 3

mock() can take also an expression which will be evaluated upon a call.

x <- 1
y <- 2
m <- mock(x + y)
m()
#> [1] 3

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.

m <- mock(1, 2)
m()
#> [1] 1
m()
#> [1] 2
m()
#> Error: too many calls to mock object and cycle set to FALSE
m <- mock(1, 2, cycle = TRUE)
m()
#> [1] 1
m()
#> [1] 2
m()
#> [1] 1
m()
#> [1] 2

If a return value is defined by an expression, this expression will be evaluated each time a cycle reaches its position.

x <- 1
y <- 2
m <- mock(1, x + y, cycle = TRUE)

m()
#> [1] 1
m()
#> [1] 3
y <- 10
m()
#> [1] 1
m()
#> [1] 11

Evaluating expression in an environment of choice

Finally, one can specify the environment where the return expression is evaluated.

x <- 1
y <- 2
e <- new.env()
m <- mock(x + y, envir = e, cycle = TRUE)

m()
#> [1] 3
e$x <- 10
m()
#> [1] 12

Integration with with_mock()

Simple integration

Using mock functions with testthat's with_mock() is pretty straightforward.

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().

m <- mock(1, 2)

m()
#> [1] 1
expect_called(m, 1)

m()
#> [1] 2
expect_called(m, 2)

And here is what happens when we get the number of calls wrong.

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.

m <- mock(1)
with_mock(summary = m, {
  summary(iris)
})
#> [1] 1

expect_call(m, 1, summary(iris))

And here is what happens if the call doesn't match.

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.

expect_args(m, 1, iris)

Here is what happens if the value turns out to be different.

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().

m <- mock(1)
with_mock(summary = m, {
  summary(object = iris)
})
#> [1] 1

expect_args(m, 1, object = iris)

Omitting the name results in an error.

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 package.

mockery/inst/doc/mocks-and-testthat.Rmd0000644000176200001440000001377713201337231017635 0ustar liggesusers--- 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. mockery/tests/0000755000176200001440000000000013173435132013067 5ustar liggesusersmockery/tests/testthat.R0000644000176200001440000000007213173435132015051 0ustar liggesuserslibrary(testthat) library(mockery) test_check("mockery") mockery/tests/testthat/0000755000176200001440000000000013201367135014726 5ustar liggesusersmockery/tests/testthat/test_stub.R0000644000176200001440000002025113201337016017060 0ustar liggesuserstestthat::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!') }) mockery/tests/testthat/test-mock-object.R0000644000176200001440000000663113200334717020230 0ustar liggesuserscontext("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") }) mockery/NAMESPACE0000644000176200001440000000043113173435132013142 0ustar liggesusers# 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) mockery/NEWS0000644000176200001440000000027413201337016012421 0ustar liggesusersv 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. mockery/R/0000755000176200001440000000000013201337231012117 5ustar liggesusersmockery/R/mockery.R0000644000176200001440000000141413173435132013722 0ustar liggesusers#' 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')) #' }) #' } NULLmockery/R/mock-object.R0000644000176200001440000000740213173435132014451 0ustar liggesusers #' 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) } mockery/R/expectations.R0000644000176200001440000000620013173435132014755 0ustar liggesusers #' 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 "")) ) } mockery/R/stub.R0000644000176200001440000001237513201337016013230 0ustar liggesusers #' 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) } mockery/vignettes/0000755000176200001440000000000013201337231013726 5ustar liggesusersmockery/vignettes/mocks-and-testthat.Rmd0000644000176200001440000001377713173435132020132 0ustar liggesusers--- 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. mockery/README.md0000644000176200001440000001040513201337016013176 0ustar liggesusers# 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. mockery/MD50000644000176200001440000000211613201367135012234 0ustar liggesuserse732efce7de7dbcc8724f7f2dbcfb1a8 *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 mockery/build/0000755000176200001440000000000013201337231013015 5ustar liggesusersmockery/build/vignette.rds0000644000176200001440000000034213201337231015353 0ustar liggesusers‹m‘Û ‚@†ÇC–Buë èt-BDÑE·‹n*é: ÝõäÙX¹9ìÌøþ9°g TÐT hh6ƒ|A®€&í«¼¯•ËDä"¯†Þ1$ÊÙ·ÔÆÙ äqÉ0±S§˜8½Fâ×#YÌ3 [ާží7óaÉtõG3,ç•”hêóQûü×+$™íø½.Ê^3`ŒŽÑƒ4ã}ÝSŠß‹vðƒî¨åþòú7Ë¢öúæí=(4dò aÆ*yP+bȼKIú¶ï>ƨ¬ãmockery/DESCRIPTION0000644000176200001440000000227013201367135013433 0ustar liggesusersPackage: 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@R: c( person("Noam", "Finkelstein", role = c("aut", "cre"), email = "noam.finkelstein@jhu.edu"), person("Lukasz", "Bartnik", rol = c("aut"), email = "l.bartnik@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 Repository: CRAN Date/Publication: 2017-11-10 18:22:21 UTC mockery/man/0000755000176200001440000000000013200334717012476 5ustar liggesusersmockery/man/mock.Rd0000644000176200001440000000521313200334717013717 0ustar liggesusers% 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)) } mockery/man/stub.Rd0000644000176200001440000000251013200334717013740 0ustar liggesusers% 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() } mockery/man/call-expectations.Rd0000644000176200001440000000254213200334717016407 0ustar liggesusers% 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)) } mockery/man/mockery.Rd0000644000176200001440000000150213200334717014434 0ustar liggesusers% 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')) }) } } mockery/LICENSE0000644000176200001440000000007613173435132012735 0ustar liggesusersYEAR: 2016 COPYRIGHT HOLDER: Noam Finkelstein, Lukasz Bartnik