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
Copy file name to clipboardExpand all lines: CLAUDE.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
6
6
7
7
testthat is R's most popular unit testing framework, used by thousands of CRAN packages. It provides functions to make testing R code as fun and addictive as possible, with clear expectations, visual progress indicators, and seamless integration with R package development workflows.
8
8
9
-
## Key Development Commands
9
+
## Key development commands
10
10
11
11
General advice:
12
12
* When running R from the console, always run it with `--quiet --vanilla`
@@ -23,7 +23,8 @@ General advice:
23
23
24
24
### Documentation
25
25
26
-
- Always run `devtools::document()` after changing any roxygen2 docs.
26
+
- Run `devtools::document()` after changing any roxygen2 docs.
Testing is easy when your functions are pure: they take some inputs and return predictable outputs. But real-world code often involves randomness, external state, graphics, user interaction, and other challenging elements. This vignette provides practical solutions for testing these tricky scenarios.
17
+
Testing is easy when your functions are pure: they take some inputs and return predictable outputs. But real-world code often involves randomness, external state, graphics, user interaction, and other challenging elements. This vignette provides practical solutions these tricky scenarios.
18
18
19
-
Other packages:
19
+
In principle, it's often possible to test these things by explicitly parameterising them as arguments to your functions so you can more easily override the default values. And where possible you should do so, especially when testing internal functions. But it's often impractical to provide arguments to explicitly control every last feature without exploding user-facing interfaces. So the techniques in this vignette will help you test all your code, regardless of where it lives and what it does.
20
20
21
-
* For testing graphical output, we recommend vdiffr.
22
-
* For testing code that uses HTTP requests we recommend vcr or httptest2.
21
+
This vignette is divided into sections based on the underlying tool you'll use:
23
22
24
-
```{r setup}
25
-
library(testthat)
26
-
```
23
+
* External state shows you how to use the withr package to handle options, environment variables, the working direction, and random number generation.
24
+
* Snapshotting shows you how to handle functions that produce user facing output including text, warnings, and errors.
25
+
* Mocking is general purpose tool when all else fails; it allows you to temporarily replace a function or method with a mockup that you can control.
26
+
* Subtests shows you how to use functions and for-loops to reduce duplication in your test code, making it easier to test that multiple part of your package have the same behaviour or follow the same interface.
27
27
28
-
## External state
28
+
To begin, there are a couple of scenarios that testthat doesn't help with, but we can happily suggest other suggest:
29
29
30
-
Tests should be isolated from global options, environment variables, and other external state that might affect behavior.
30
+
* If you need to test graphical output, {vdiffr}. vdiffr is used to test ggplot2, and incorporates everything we know about high-quality graphics tests that minimise false positives.
31
31
32
-
### Output affected by RNG
32
+
* If you need to test HTTP requests, we recommend using {vcr} or {httptest2}.
33
33
34
-
Random number generation can make tests non-deterministic. Use `withr::local_seed()` to ensure reproducible results within your tests.
test_that("random sample has expected properties", {
51
-
withr::local_seed(123)
52
-
x <- sample(1:100, 10)
53
-
expect_length(x, 10)
54
-
expect_true(all(x %in% 1:100))
55
-
# This will always pass now:
56
-
expect_equal(x[1], 31)
57
-
})
58
-
```
42
+
If your code depends on global options, environment variables, or the working directory. In most cases, it's good practice to make these dependencies explicit by making them the default value of an argument so you can control directly in your tests. However, sometimes you are testing deeply embedded code and it would be painful to thread the values all the way through to the right place. In this case can temporarily override with withr functions:
59
43
60
-
### Global options
44
+
* Temporarily change options with `withr::local_options()`.
45
+
* Temporarily change env vars with `withr::local_envvar()`.
46
+
* Temporarily change the working directory with `withr::local_dir()`.
Random number generation also falls into the same bucket because it depends on the value of the special `.Random.seed` variable which is updated whenever you generate a random number. You can temporarily change this seed and reproducibly generate "random" numbers with `withr::local_seed()`.
If you find yourself using the same `local_` calls in multiple places, you may want to create your own helper function. This is straightforward once you know how these functions. The most important thing to know is that they are all wrappers around `on.exit()` which runs code when a function exits. The question is: which function? By default, it's the function that calls `withr::local_*()`. But obvious that's not going to work if you write a helper function:
98
84
99
85
```{r}
100
-
test_that("function works in different directories", {
101
-
withr::local_dir(withr::local_tempdir())
102
-
# Test code that depends on working directory
103
-
writeLines("test content", "temp_file.txt")
104
-
expect_true(file.exists("temp_file.txt"))
105
-
# File will be cleaned up automatically
86
+
local_my_helper <- function() {
87
+
withr::local_options(x = 10)
88
+
}
89
+
90
+
local({
91
+
local_my_helper()
92
+
getOption("x")
106
93
})
107
94
```
108
95
109
-
### Local wrappers
110
-
111
-
If you want to make your own function, you should take a `frame` argument. frame is an environment on the call stack, i.e. it's the execution environment of some function, and the local effects will be undone when that function is completed. Underneath the hood this is all wrappers around `on.exit()`.
96
+
To resolve this problem we need to capture the calling frame for our helper function. A **frame** is an environment on the call stack, i.e. the execution environment of some function that lead to the current call.
We strongly recommend giving such functions a `local_` prefix to clearly communicate that they have "local" effects.
110
+
117
111
## Errors and user-facing text
118
112
119
-
Error messages, warnings, and other user-facing text should be tested to ensure they're helpful and consistent. Snapshots are perfect for this.
113
+
Error messages, warnings, and other user-facing text should be tested to ensure they're helpful and consistent. Obviously you can't test this 100% automatically, but you can ensure that such messaging is clearly shown in PRs, so another human can take a look. This is exactly the point of snapshot tests.
120
114
121
-
### Testing error messages
115
+
Snapshot tets are particularly important when testing complex error messages.
When generating sophisticated error messages that use cli's interpolation and formatting features, snapshot tests are essential for ensuring the messages render correctly with proper styling and content.
@@ -153,9 +173,15 @@ The same idea applies to messages and warnings.
153
173
154
174
### `local_reproducible_output()`
155
175
176
+
By default, testthat sets a number of options that simplify and standardise output:
156
177
178
+
* The console width is set to 80.
179
+
* Crayon/cli ANSI colouring and hyperlinks are suppressed.
180
+
* Unicode characters are suppressed.
157
181
158
-
### Transformations
182
+
These are sound defaults that we have found useful to minimise spurious diffs between tests run in different environment. But it's sometimes necessary to override them in order to test various output features. So, if necessary, you can override these settings by calling `local_reproducible_output()`. Read its docs to learn more.
183
+
184
+
### Transforms
159
185
160
186
Sometimes part of the output varies in ways that you can't easily control. There are two techniques you can use: mocking (described next) or the `transform` output.
161
187
@@ -172,42 +198,28 @@ Sometimes part of the output varies in ways that you can't easily control. There
172
198
* Sometimes easier or more clear to mock a function rather than setting options/env vars. And generally just tickling some branch that would otherwise be hard to reach.
But we generally recommend using `rlang::is_interactive()`. Can be manually overridden by `rlang_interactive` option, whih is automatically set inside of tests.
But we generally recommend using `rlang::is_interactive()`. Can be manually overridden by `rlang_interactive` option, whih is automatically set inside of tests.More
0 commit comments