Skip to content

Commit fa69ee8

Browse files
committed
Update custom expectations vignette.
1 parent f3291e7 commit fa69ee8

File tree

1 file changed

+30
-12
lines changed

1 file changed

+30
-12
lines changed

vignettes/custom-expectation.Rmd

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ expect_length <- function(object, n) {
3030
# 2. Check if expectations are violated
3131
act_n <- length(act$val)
3232
if (act_n != n) {
33-
msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n)
33+
msg <- c(
34+
sprintf("Expected %s to have length %i.", act$lab, n),
35+
sprintf("Actual length: %i.", act$n)
36+
)
3437
return(fail(msg))
3538
}
3639
@@ -41,7 +44,7 @@ expect_length <- function(object, n) {
4144

4245
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 🙂.
4346

44-
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 state both what the object is and what you expected.
47+
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.
4548

4649
Also note that you need to use `return(fail())` here. You won't 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.
4750

@@ -98,13 +101,19 @@ expect_vector_length <- function(object, n) {
98101
# It's non-trivial to check if an object is a vector in base R so we
99102
# use an rlang helper
100103
if (!rlang::is_vector(act$val)) {
101-
msg <- sprintf("%s is a %s, not a vector", act$lab, typeof(act$val))
104+
msg <- c(
105+
sprintf("Expected %s to be a vector", act$lab),
106+
sprintf("Actual: %s", typeof(act$val))
107+
)
102108
return(fail(msg))
103109
}
104110
105111
act_n <- length(act$val)
106112
if (act_n != n) {
107-
msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n)
113+
msg <- c(
114+
sprintf("Expected %s to have length %i.", act$lab, n),
115+
sprintf("Actual length: %i.", act_n)
116+
)
108117
return(fail(msg))
109118
}
110119
@@ -131,19 +140,22 @@ expect_s3_class <- function(object, class) {
131140
act <- quasi_label(rlang::enquo(object))
132141
133142
if (!is.object(act$val)) {
134-
return(fail(sprintf("%s is not an object.", act$lab)))
143+
msg <- sprintf("Expected %s to be an object.", act$lab)
144+
return(fail(msg))
135145
}
136146
137147
if (isS4(act$val)) {
138-
return(fail(sprintf("%s is an S4 object, not an S3 object.", act$lab)))
148+
msg <- c(
149+
sprintf("Expected %s to be an S3 object.", act$lab),
150+
"Actual: S4 object"
151+
)
152+
return(fail(msg))
139153
}
140154
141155
if (!inherits(act$val, class)) {
142-
msg <- sprintf(
143-
"%s inherits from %s not %s.",
144-
act$lab,
145-
paste0(class(object), collapse = "/"),
146-
paste0(class, collapse = "/")
156+
msg <- c(
157+
sprintf("Expected %s to inherit from %s.", act$lab, class),
158+
sprintf("Actual class: %s", class(act$val))
147159
)
148160
return(fail(msg))
149161
}
@@ -165,7 +177,13 @@ expect_s3_class(x3, "integer")
165177
expect_s3_class(x3, "factor")
166178
```
167179

168-
Note that I also check that the `class` argument must be a string. This is an error, not a failure, because it suggests you're using the function incorrectly.
180+
Note the variety of messages:
181+
182+
* When `object` isn't an object, we only need to say what we expect.
183+
* When `object` isn't an S3 object, we know it's an S4 object.
184+
* When `inherits()` is `FALSE`, we provide the actual _class_, since that's most informative.
185+
186+
I also check that the `class` argument must be a string. This is an error, not a failure, because it suggests you're using the function incorrectly.
169187

170188
```{r}
171189
#| error: true

0 commit comments

Comments
 (0)