Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ Config/testthat/parallel: true
Config/testthat/start-first: watcher, parallel*
Encoding: UTF-8
Roxygen: list(markdown = TRUE, r6 = FALSE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
6 changes: 6 additions & 0 deletions R/mock-oo.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#' Mock S3 and S4 methods
#'
#' @description
#' These functions allow you to temporarily override S3 and S4 methods that
#' already exist. It works by using [registerS3method()]/[setMethod()] to
#' temporarily replace the original definition.
#'
#' Learn more about mocking in `vignette("mocking")`.
#'
#' @param generic A string giving the name of the generic.
#' @param signature A character vector giving the signature of the method.
#' @param definition A function providing the method definition.
Expand Down Expand Up @@ -67,12 +70,15 @@ local_mocked_s4_method <- function(

#' Mock an R6 class
#'
#' @description
#' This function allows you to temporarily override an R6 class definition.
#' It works by creating a subclass then using [local_mocked_bindings()] to
#' temporarily replace the original definition. This means that it will not
#' affect subclasses of the original class; please file an issue if you need
#' this.
#'
#' Learn more about mocking in `vignette("mocking")`.
#'
#' @export
#' @param class An R6 class definition.
#' @param public,private A named list of public and private methods/data.
Expand Down
4 changes: 1 addition & 3 deletions R/mock2.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
#' state (i.e. reading a value from a file or a website, or pretending a package
#' is or isn't installed).
#'
#' These functions represent a second attempt at bringing mocking to testthat,
#' incorporating what we've learned from the mockr, mockery, and mockthat
#' packages.
#' Learn more in `vignette("mocking")`.
#'
#' # Use
#'
Expand Down
2 changes: 1 addition & 1 deletion man/expect_vector.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions man/local_mocked_bindings.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/local_mocked_r6_class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/local_mocked_s3_method.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions vignettes/challenging-tests.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,25 @@ test_that("user must respond y or n", {
})
```

If you don't care about reproducing the output of `continue()` and just want to recreate its return value, you can use `mock_output_sequence()`. This creates a function that returns the input supplied to `mock_output_sequence()` in sequence: the first input at the first call, the second input at the second call, etc. The following code shows how it works and how you might use it to test `readline()`:

```{r}
f <- mock_output_sequence(1, 12, 123)
f()
f()
f()
```

And

```{r}
test_that("user must respond y or n", {
local_mocked_bindings(readline = mock_output_sequence("x", "y"))
expect_true(continue("This is dangerous"))
})
```


If you were testing the behavior of some function that uses `continue()`, you might want to mock `continue()` instead of `readline()`. For example, the function below requires user confirmation before overwriting an existing file. In order to focus our tests on the behavior of just this function, we mock `continue()` to return either `TRUE` or `FALSE` without any user messaging.

```{r}
Expand Down
41 changes: 41 additions & 0 deletions vignettes/custom-expectation.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,47 @@ This vignette shows you how to write your own expectations. Custom expectations

In this vignette, you'll learn about the three-part structure of expectations, how to test your custom expectations, see a few examples, and, if you're writing a lot of expectations, learn how to reduce repeated code.

## Do you need it?

But before you read the rest of the vignette and dive into the full details of creating a 100% correct expectation, consider if you can get away with a simpler wrapper. If you're just customising an existing expectation by changing some defaults, you're fine:

```{r}
expect_df <- function(tbl) {
expect_s3_class(tbl, "data.frame")
}
```

If you're combining multiple expectations, you can introduce a subtle problem. For example, take this expectation from tidytext:

```{r}
# from tidytext
expect_nrow <- function(tbl, n) {
expect_s3_class(tbl, "data.frame")
expect_equal(nrow(tbl), n)
}
```

If we use it in a test you can see there's an issue:

```{r}
#| error: true
test_that("success", {
expect_nrow(mtcars, 32)
})
test_that("failure 1", {
expect_nrow(mtcars, 30)
})
test_that("failure 2", {
expect_nrow(matrix(1:5), 2)
})
```

Each of these tests contain a single expectation, but they report a total of two successes and failures. It would be confusing if testthat didn't report these numbers correctly. But as a helper in your package, it's probably not a big deal.

You might also notice that these failures generate a backtrace whereas built-in expectations don't. Again, it's not a big deal because the backtrace is correct, it's just not needed.

These are both minor issues, so if they don't bother you, you can save yourself some pain by not reading this vignette 😀.

## Expectation basics

An expectation has three main parts, as illustrated by `expect_length()`:
Expand Down