This vignette shows you how to create custom expectations that work
identically to the built-in expect_
functions. Since these
functions will need to be loaded when your package is loaded for
testing, it is recommended that expect_
functions be
defined in test-helpers.R
in your packages R/
directory.
There are three main parts to writing an expectation, as illustrated
by expect_length()
:
<- function(object, n) {
expect_length # 1. Capture object and label
<- quasi_label(rlang::enquo(object), arg = "object")
act
# 2. Call expect()
$n <- length(act$val)
actexpect(
$n == n,
actsprintf("%s has length %i, not length %i.", act$lab, act$n, n)
)
# 3. Invisibly return the value
invisible(act$val)
}
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.
By convention, the first argument to every expect_
function is called object
, and you capture it’s value
(val
) and label (lab
) with
act <- quasi_label(enquo(object))
, where
act
is short for actual.
Next, you should verify the expectation. This often involves a little
computation (here just figuring out the length
), and you
should typically store the results back into the act
object.
Next you call expect()
. This has two arguments:
ok
: was the expectation successful? This is usually
easy to write
failure_message
: What informative error message
should be reported to the user so that they can diagnose the problem.
This is often hard to write!
For historical reasons, most built-in expectations generate these
with sprintf()
, but today I’d recommend using the glue package
Expectation functions are called primarily for their side-effects
(triggering a failure), so should invisibly return their input,
act$val
. This allows expectations to be chained:
%>%
mtcars expect_type("list") %>%
expect_s3_class("data.frame") %>%
expect_length(11)
succeed()
and fail()
For expectations with more complex logic governing when success or
failure occurs, you can use succeed()
and
fail()
. These are simple wrappers around
expect()
that allow you to write code that looks like
this:
<- function(object, n) {
expect_length <- quasi_label(rlang::enquo(object), arg = "object")
act
$n <- length(act$val)
actif (act$n == n) {
succeed()
return(invisible(act$val))
}
<- sprintf("%s has length %i, not length %i.", act$lab, act$n, n)
message fail(message)
}
Use the expectations expect_success()
and
expect_failure()
to test your expectation.
test_that("length computed correctly", {
expect_success(expect_length(1, 1))
expect_failure(expect_length(1, 2), "has length 1, not length 2.")
expect_success(expect_length(1:10, 10))
expect_success(expect_length(letters[1:5], 5))
})#> Test passed 🥇