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: vignettes/custom-expectation.Rmd
+40-35Lines changed: 40 additions & 35 deletions
Original file line number
Diff line number
Diff line change
@@ -48,9 +48,11 @@ If we use it in a test you can see there's an issue:
48
48
test_that("success", {
49
49
expect_nrow(mtcars, 32)
50
50
})
51
+
51
52
test_that("failure 1", {
52
53
expect_nrow(mtcars, 30)
53
54
})
55
+
54
56
test_that("failure 2", {
55
57
expect_nrow(matrix(1:5), 2)
56
58
})
@@ -64,35 +66,38 @@ These are both minor issues, so if they don't bother you, you can save yourself
64
66
65
67
## Expectation basics
66
68
67
-
An expectation has three main parts, as illustrated by `expect_length()`:
69
+
An expectation has four main parts, as illustrated by `expect_length()`:
68
70
69
71
```{r}
70
72
expect_length <- function(object, n) {
71
73
# 1. Capture object and label
72
74
act <- quasi_label(rlang::enquo(object))
73
-
74
-
# 2. Check if expectations are violated
75
+
75
76
act_n <- length(act$val)
76
77
if (act_n != n) {
77
78
msg <- c(
78
79
sprintf("Expected %s to have length %i.", act$lab, n),
79
80
sprintf("Actual length: %i.", act_n)
80
81
)
81
-
return(fail(msg))
82
+
# 2. Fail if expectations are violated
83
+
fail(msg)
84
+
} else {
85
+
# 3. Pass if expectations are met
86
+
pass()
82
87
}
83
88
84
-
# 3. Pass when expectations are met
85
-
pass(act$val)
89
+
# 4. Invisibly return the input value
90
+
invisible(act$val)
86
91
}
87
92
```
88
93
89
94
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`) used to make failure messages as informative as possible. This is a pattern that exists for fairly esoteric reasons; you don't need to understand it, just copy and paste it.
90
95
91
-
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.
96
+
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 an nested if-else statement. That makes it easier to write informative failure messages that first describe what was expected and then what was actually seen.
92
97
93
-
Note that you need to use `return(fail())` here. If you don't, your expectation might end up failing multiple times or both failing and succeeding. You won't see these problems when interactively testing your expectation, but forgetting to `return()` can lead to incorrect fail and pass counts in typical usage. In the next section, you'll learn how to test your expectation to avoid this issue.
98
+
If the object is as expected, call `pass()`. This ensures that a success will be registered in the test reporter.
94
99
95
-
Finally, if the object is as expected, call `pass()` with `act$val`. This is good practice because expectation functions are called primarily for their side-effects (triggering a failure), and returning the value allows expectations to be piped together:
100
+
Finally, return the input value (`act$val`) invisibly. This is good practice because expectations are called primarily for their side-effects (triggering a failure), and returning the value allows expectations to be piped together:
0 commit comments