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 shows you how to write your expectations that work identically to the built-in `expect_` functions.
20
-
21
-
You can use these either locally by putting them in a helper file, or export them from your package.
19
+
This vignette shows you how to write your expectations. You can use within your package by putting them in a helper file, or share them with others by exporting them from your package.
22
20
23
21
## Expectation basics
24
22
25
-
There are three main parts to writing an expectation, as illustrated by `expect_length()`:
23
+
An expectation has three main parts, as illustrated by `expect_length()`:
msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n)
36
34
return(fail(msg))
37
35
}
38
36
39
-
# 3. Pass
37
+
# 3. Pass when expectations are
40
38
pass(act$val)
41
39
}
42
40
```
43
41
44
-
### Capture value and label
42
+
The first step in any expectation is to use `quasi_label()` to capture both the value (`$val`) of the first argument and a label (`$lab`) to use failure messages. This is a pattern that exists to support fairly esoteric testthat features; you don't need to understand, just copy and paste it 🙂.
43
+
44
+
Next, you need to fail, for each way that the `object` violates our expectation. In my experience it's easier to check for problems one by one, because that yields the most informative failure messages. Note that it's really important to `return(fail())` here. You wont see the problem when interactively testing your function because when run outside of `test_that()`, `fail()` throws an error, causing the function to terminate early. When running inside of `test_that()` however, `fail()` does not stop execution because we want to collect all failures in a given test.
45
+
46
+
Finally, if the object is expected, call `pass()` with the input value (usually `act$val`). Returning the input value is good practice since expectation functions are called primarily for their side-effects (triggering a failure). This allows expectations to be chained:
45
47
46
-
The first step in any expectation is to capture the actual object, and generate a label for it to use if a failure occur. All testthat expectations support quasiquotation so that you can unquote variables. This makes it easier to generate good labels when the expectation is called from a function or within a for loop.
48
+
```{r}
49
+
mtcars |>
50
+
expect_type("list") |>
51
+
expect_s3_class("data.frame") |>
52
+
expect_length(11)
53
+
```
47
54
48
-
By convention, the first argument to every `expect_` function is called `object`, and you capture its value (`val`) and label (`lab`) with `act <- quasi_label(enquo(object))`, where `act` is short for actual (in constrast to expected).
55
+
## Testing your expectations
49
56
50
-
### Verify the expectation
57
+
testthat comes with three expectations designed specifically to test expectations: `expect_success()` and `expect_failure()`:
51
58
52
-
Now we can check if our expectation is met and return `fail()` if not. The most challenging job here is typically generating the error message because you want it to be as self-contained as possible. This means it should typically give both the expected and actual value, along with the name of the object passed to the expectation. testthat expectations use `sprintf()`, but if you're familiar with {glue}, you might want to use that instead.
59
+
*`expect_success()` checks that your expectation emits exactly one success and zero failures.
60
+
*`expect_failure()` checks that your expectation emits exactly one failure and zero successes.
61
+
*`expect_failure_snapshot()` captures the failure message in a snapshot, making it easier to review if it's useful or not.
53
62
54
-
More complicated expectations will have more `if` statements. For example, we might want to make our `expect_length()` function include an assertion that `object` is a vector:
63
+
It's important to check that expectations return either one failure or one success because the ensures that reporting is correct. If you
Note that it's really important to `return(fail())` here. You wont see the problem when interactively testing your function because when run outside of `test_that()`, `fail()` throws an error, causing the function to terminate early. When running inside of `test_that()` however, `fail()` does not stop execution because we want to collect all failures in a given test.
103
+
To make your failure messages as actionable as possible, state both what the object is and what you expected:
76
104
77
-
### Pass the test
105
+
```{r}
106
+
#| error: true
107
+
expect_vector_length(mean, 10)
108
+
expect_vector_length(mtcars, 15)
109
+
```
78
110
79
-
If no assertions fail, call `pass()` with the input value (usually `act$val`). Returning the input value is good practice since expectation functions are called primarily for their side-effects (triggering a failure). This allows expectations to be chained:
111
+
### `expect_s3_class()`
112
+
113
+
As another example, imagine if you're checking to see if an object inherits from an S3 class. In R, there's no direct way to tell if an object is an S3 object: you can confirm that it's an object, then that it's not an S4 object. So you might organise your test this way:
testthat comes with three expectations designed specifically to test expectations: `expect_success()` and `expect_failure()`:
153
+
## Repeated code
91
154
92
-
*`expect_success()` checks that your expectation emits exactly one success and zero failures.
93
-
*`expect_failure()` checks that your expectation emits exactly one failure and zero successes.
94
-
*`expect_failure_snapshot()` captures the failure message in a snapshot, making it easier to review if it's useful or not.
155
+
As you write more expectations, you might discover repeated code that you want to extract out in to a helper. For example, testthat has `expect_true()`, `expect_false()`, and `expect_null()` which are special cases of `expect_equal()`
95
156
96
157
```{r}
97
-
test_that("expect_length works as expected", {
98
-
x <- 1:10
99
-
expect_success(expect_length(x, 10))
100
-
expect_failure(expect_length(x, 11))
158
+
expect_true <- function(object) {
159
+
act <- quasi_label(enquo(object))
160
+
expect_constant_(act, TRUE, ignore_attr = TRUE)
161
+
}
162
+
expect_false <- function(object) {
163
+
act <- quasi_label(enquo(object))
164
+
expect_constant_(act, FALSE, ignore_attr = TRUE)
165
+
}
166
+
expect_null <- function(object, label = NULL) {
167
+
act <- quasi_label(enquo(object))
168
+
expect_constant_(act, NULL)
169
+
}
101
170
102
-
expect_snapshot_failure(expect_length(x, 11))
103
-
})
171
+
expect_constant_ <- function(
172
+
act,
173
+
constant,
174
+
ignore_attr = TRUE,
175
+
trace_env = caller_env()
176
+
) {
177
+
comp <- waldo::compare(
178
+
act$val,
179
+
constant,
180
+
x_arg = "actual",
181
+
y_arg = "expected",
182
+
ignore_attr = ignore_attr
183
+
)
184
+
if (length(comp) != 0) {
185
+
msg <- sprintf(
186
+
"%s is not %s\n\n%s",
187
+
act$lab,
188
+
format(constant),
189
+
paste0(comp, collapse = "\n\n")
190
+
)
191
+
return(fail(msg, info = info, trace_env = trace_env))
0 commit comments