You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This vignette is a quick reference guide for testing challenging functions. It's organised by the problem, rather than technique used to solve it, so you can quickly skim the whole vignette, spot the problem your facing, then learn more about useful tools for solving it.
23
+
This vignette is a quick reference guide for testing challenging functions. It's organised by the problem, rather than technique used to solve it, so you can quickly skim the whole vignette, spot the problem you're facing, then learn more about useful tools for solving it.
If you're trying to test functions that rely on HTTP requests, we recommend using {vcr} or {httptest2}. These packages provide the ability to record and then later reply HTTP requests so that you can test without an active internet connection. If your package is going to CRAN, we highly recommend either using one of these packages or using `skip_on_cran()` for your internetfacing tests. This ensures that your package won't break on CRAN just because the service you're using is temporarily down.
66
+
If you're trying to test functions that rely on HTTP requests, we recommend using {vcr} or {httptest2}. These packages provide the ability to record and then later reply HTTP requests so that you can test without an active internet connection. If your package is going to CRAN, we highly recommend either using one of these packages or using `skip_on_cran()` for your internet-facing tests. This ensures that your package won't break on CRAN just because the service you're using is temporarily down.
Copy file name to clipboardExpand all lines: vignettes/mocking.Rmd
+8-8Lines changed: 8 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -28,7 +28,7 @@ testthat supports mocking primarily through `local_mocked_bindings()` for mockin
28
28
29
29
## Getting started with mocking
30
30
31
-
Lets begin by motivating mocking with a simple example. Imagine you're writing a function like `rlang::check_installed()`. The goal of this function is to check if a package is installed, and if not, give a nice error message. It also takes an option `min_version` argument which you can use to enforce a version constraint. A simple base R implementation might look something like this:
31
+
Let's begin by motivating mocking with a simple example. Imagine you're writing a function like `rlang::check_installed()`. The goal of this function is to check if a package is installed, and if not, give a nice error message. It also takes an option `min_version` argument which you can use to enforce a version constraint. A simple base R implementation might look something like this:
Now that we've written this function, we want to test it. There a lot of ways we might tackle this, but I think it's response to start by testing the case without `min_version`. To do this we need to come up with a package we know is installed, and a package we know isn't installed:
54
+
Now that we've written this function, we want to test it. There a lot of ways we might tackle this, but I think it's reasonable to start by testing the case without `min_version`. To do this we need to come up with a package we know is installed, and a package we know isn't installed:
55
55
56
56
```{r}
57
57
test_that("check_installed() checks package is installed", {
Again, this is probably save (since I'm unlikely to release 90+ new versions of testthat), but if you look at the snapshot message carefully, you'll notice that it includes the current version of testthat. That means every time a new version of testthat comes out, we'll have to update the snapshot. We could use the `transform` argument to fix that:
74
+
Again, this is probably safe (since I'm unlikely to release 90+ new versions of testthat), but if you look at the snapshot message carefully, you'll notice that it includes the current version of testthat. That means every time a new version of testthat comes out, we'll have to update the snapshot. We could use the `transform` argument to fix that:
But it's starting to feel like we've accumulated a bunch of potentially fragile hacks. So lets see how we could could make these tests more robust with mocking. First we need to add `requireNamspace` and `packageVersion` bindings in our package. This is needed because `requireNamespace` and `packageVersion` are base functions,
87
+
But it's starting to feel like we've accumulated a bunch of potentially fragile hacks. So let's see how we could could make these tests more robust with mocking. First we need to add `requireNamspace` and `packageVersion` bindings in our package. This is needed because `requireNamespace` and `packageVersion` are base functions:
88
88
```{r}
89
89
requireNamespace <- NULL
90
90
packageVersion <- NULL
@@ -102,7 +102,7 @@ test_that("check_installed() checks package is installed", {
102
102
})
103
103
```
104
104
105
-
For the second test, we mock `requireNamepace()` to return `TRUE`, and then `packageVersion()` to return a fixed version number. Together this simulated that version 2.0.0 of any package is installed and makes our snapshot rely only on the state set within the test.
105
+
For the second test, we mock `requireNamepace()` to return `TRUE`, and then `packageVersion()` to return a fixed version number. Together this simulates that version 2.0.0 of any package is installed and makes our snapshot rely only on the state set within the test.
To give you a bit more experience with mocking, this section looks at a few places where we use mocking throughout the tidyverse. Note that these are all a little complex; this is the nature of mocking: if you can use a simpler technique, you should. So mocking on comes up for otherwise intractable problems.
121
+
To give you a bit more experience with mocking, this section looks at a few places where we use mocking throughout the tidyverse. Note that these are all a little complex; this is the nature of mocking: if you can use a simpler technique, you should. So mocking only comes up for otherwise intractable problems.
To finish up, it's worth discussing how mocking works. It took us some iteration (`testthat::with_mock()`, as well as {mockery}, {mockr}, and {mockthat} packages) to get to current state and understanding how it works will help you to understand some of the tradeoffs.
205
205
206
-
The fundamental tension of mocking is that you want it to actually work (i.e. temporarily modify the implementation of a function), but also be "hygenic", i.e. it should only allow you to modify functions that you own, not arbitarily affect any code. To achieve this goal `local_mocked_bindings()` works by modifying an environment that you own: your package's namespace environment. (If you've forgotten exactly what these are, you can refresh your memory at <https://adv-r.hadley.nz/environments.html#special-environments>.) So you can imagine mocking works something like this if you're temporarily replacing the value of `my_function` with a `new` implementation:
206
+
The fundamental tension of mocking is that you want it to actually work (i.e. temporarily modify the implementation of a function), but also be "hygienic", i.e. it should only allow you to modify functions that you own, not arbitrarily affect any code. To achieve this goal `local_mocked_bindings()` works by modifying an environment that you own: your package's namespace environment. (If you've forgotten exactly what these are, you can refresh your memory at <https://adv-r.hadley.nz/environments.html#special-environments>.) So you can imagine mocking works something like this if you're temporarily replacing the value of `my_function` with a `new` implementation:
207
207
208
208
```{r}
209
209
#| eval: false
@@ -223,4 +223,4 @@ This leads to the two limitations of `local_mocked_bindings()`:
223
223
224
224
1.`::` doesn't use the package namespace, so if you want to mock a function from another package that you call with `::`, you either have to switch from `pkg::fun()` to `fun()` by importing `fun` into your `NAMESPACE` (e.g. with `@importFrom pkg fun`), or create your own wrapper function that you can mock. Typically, one of these options will feel fairly natural.
225
225
226
-
Overall these limitations feel correct to me: `local_mocked_bindings()` makes it easy to temporarily change the implementation of functions that you have written, while offering work arounds to override the implementations of functions that you others have written in the scope of your package.
226
+
Overall these limitations feel correct to me: `local_mocked_bindings()` makes it easy to temporarily change the implementation of functions that you have written, while offering workarounds to override the implementations of functions that you others have written in the scope of your package.
Copy file name to clipboardExpand all lines: vignettes/test-fixtures.Rmd
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -86,7 +86,7 @@ This is particularly useful when you need to chain together multiple `local_` fu
86
86
87
87
## Foundations
88
88
89
-
Before we go further, lets lay some foundations to help you understand how `local_` functions work. We'll motivate the discussion with a `sloppy()` function that prints a number with a specific number of significant digits by adjusting an R option:
89
+
Before we go further, let's lay some foundations to help you understand how `local_` functions work. We'll motivate the discussion with a `sloppy()` function that prints a number with a specific number of significant digits by adjusting an R option:
0 commit comments