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
The first step in any expectation is to use `quasi_label()` to capture a "labelled value", i.e., a list that contains both the value (`$val`) for testing and a label (`$lab`) for messaging. This is a pattern that exists for fairly esoteric reasons; you don't need to understand it, just copy and paste it 🙂.
46
+
The first step in any expectation is to use `quasi_label()` to capture a "labeled value", i.e., a list that contains both the value (`$val`) for testing and a label (`$lab`) for messaging. This is a pattern that exists for fairly esoteric reasons; you don't need to understand it, just copy and paste it 🙂.
47
47
48
48
Next you need to check each way that `object` could violate the expectation. In this case, there's only one check, but in more complicated cases there can be multiple checks. In most cases, it's easier to check for violations one by one, using early returns to `fail()`. This makes it easier to write informative failure messages that first describe what was expected and then what was actually seen.
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.
204
+
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 the current state, and understanding how it works will help you 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 "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:
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:
This leads to the two limitations of `local_mocked_bindings()`:
221
221
222
-
1. The package namespace is locked, which means that you can't add new bindings to it. That means if you want to mock base functions, you have to provide some binding that can be overridden. The easiest way to do this is (e.g.)`mean <- NULL`. This creates a binding that `local_mocked_bindings()` can modify, but because of R's [lexical scoping rules](https://adv-r.hadley.nz/functions.html#functions-versus-variables) this doesn't affect ordinary calls to `mean()`.
222
+
1. The package namespace is locked, which means that you can't add new bindings to it. That means if you want to mock base functions, you have to provide some binding that can be overridden. The easiest way to do this is (e.g.,`mean <- NULL`). This creates a binding that `local_mocked_bindings()` can modify, but because of R's [lexical scoping rules](https://adv-r.hadley.nz/functions.html#functions-versus-variables) this doesn't affect ordinary calls to `mean()`.
223
223
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.
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
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 others have written in the scope of your package.
If Pandoc is not available when `convert_markdown_to_html()` executes, it throws an error *unless* it appears to be part of a test run, in which case the test is skipped.
101
-
This is an alternative to implementing a custom skipper, e.g. `skip_if_no_pandoc()`, and inserting it into many of pkgdown's tests.
101
+
This is an alternative to implementing a custom skipper, e.g.,`skip_if_no_pandoc()`, and inserting it into many of pkgdown's tests.
102
102
103
103
We don't want pkgdown to have a runtime dependency on testthat, so pkgdown includes a copy of `testthat::is_testing()`:
Copy file name to clipboardExpand all lines: vignettes/snapshotting.Rmd
+6-6Lines changed: 6 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -237,7 +237,7 @@ test_that("actionable feedback if some or all arguments named", {
237
237
238
238
Sometimes part of the output varies in ways that you can't easily control. In many cases, it's convenient to use mocking (`vignette("mocking")`) to ensure that every run of the function always produces the same output. In other cases, it's easier to manipulate the text output with a regular expression or similar. That's the job of the `transform` argument which should be passed a function that takes a character vector of lines, and returns a modified vector.
239
239
240
-
This type of problem often crops up when you are testing a function that gives feedback about a path. In your tests, you'll typically use a temporary path (e.g. from `withr::local_tempfile()`) so if you display the path in a snapshot, it will be different every time.
240
+
This type of problem often crops up when you are testing a function that gives feedback about a path. In your tests, you'll typically use a temporary path (e.g., from `withr::local_tempfile()`) so if you display the path in a snapshot, it will be different every time.
241
241
For example, consider this "safe" version of `writeLines()` that requires you to explicitly opt-in to overwriting an existing file:
242
242
243
243
```{r}
@@ -308,20 +308,20 @@ Now even though the path varies, the snapshot does not.
308
308
By default, testthat sets a number of options that simplify and standardise output:
309
309
310
310
* The console width is set to 80.
311
-
* Crayon/cli ANSI colouring and hyperlinks are suppressed.
311
+
* Crayon/cli ANSI coloring and hyperlinks are suppressed.
312
312
* Unicode characters are suppressed.
313
313
314
-
These are sound defaults that we have found useful to minimise spurious difference between tests run in different environments. However, there are times when you want to deliberately test different widths, or ANSI escapes, or unicode characters, so you can override the defaults with `local_reproducible_output()`.
314
+
These are sound defaults that we have found useful to minimize spurious differences between tests run in different environments. However, there are times when you want to deliberately test different widths, or ANSI escapes, or Unicode characters, so you can override the defaults with `local_reproducible_output()`.
315
315
316
316
### Snapshotting graphics
317
317
318
-
If you need to test graphical output, use {vdiffr}. vdiffr is used to test ggplot2, and incorporates everything we know about high-quality graphics tests that minimise false positives. Graphics testing is still often fragile, but using vdiffr means you will avoid all the problems we know how to avoid.
318
+
If you need to test graphical output, use {vdiffr}. vdiffr is used to test ggplot2, and incorporates everything we know about high-quality graphics tests that minimize false positives. Graphics testing is still often fragile, but using vdiffr means you will avoid all the problems we know how to avoid.
319
319
320
320
### Snapshotting values
321
321
322
322
`expect_snapshot()` is the most used snapshot function because it records everything: the code you run, printed output, messages, warnings, and errors.
323
-
If you care about the return value rather than any side-effects, you might want to use `expect_snapshot_value()` instead.
324
-
It offers a number of serialisation approaches that provide a tradeoff between accuracy and human readability.
323
+
If you care about the return value rather than any sideeffects, you might want to use `expect_snapshot_value()` instead.
324
+
It offers a number of serialization approaches that provide a tradeoff between accuracy and human readability.
Making external state explicit is often worthwhile, because it makes it more clear exactly what inputs determine the outputs of your function. But it's simply not possible in many cases. That's where test fixtures come in: they allow you to temporarily change global state in order to test your function. Test fixture is a pre-existing term in the software engineering world (and beyond):
274
+
Making external state explicit is often worthwhile because it makes it clearer exactly what inputs determine the outputs of your function. But it's simply not possible in many cases. That's where test fixtures come in: they allow you to temporarily change global state to test your function. Test fixture is a pre-existing term in the software engineering world (and beyond):
275
275
276
276
> A test fixture is something used to consistently test some item, device, or piece of software.
277
277
>
@@ -360,7 +360,7 @@ Setup code is typically best used to create external resources that are needed b
360
360
361
361
## Other challenges
362
362
363
-
A collection of miscellaneous problems that I don't know where else to describe:
363
+
A collection of miscellaneous problems that don't fit elsewhere:
364
364
365
365
- There are a few base functions that are hard to test because they depend on state that you can't control. One such example is `interactive()`: there's no way to write a test fixture that allows you to pretend that interactive is either `TRUE` or `FALSE`. So we now usually use `rlang::is_interactive()` which can be controlled by the `rlang_interactive` option.
0 commit comments