# Custom expectations

This vignette shows you how to create custom expectations that work identically to the built-in `expect_` functions.

## Creating an expectation

There are three main parts to writing an expectation, as illustrated by `expect_length()`:

    expect_length <- function(object, n) {
      # 1. Capture object and label
      act <- quasi_label(rlang::enquo(object), arg = "object")

      # 2. Call expect()
      act$n <- length(act$val)
      expect(
        act$n == n,
        sprintf("%s has length %i, not length %i.", act$lab, act$n, n)
      )

      # 3. Invisibly return the value
      invisible(act$val)
    }

## Quasi-labelling

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.

### Verify the expectation

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:

1. `ok`: was the expectation successful? This is usually easy to write

2. `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

### Invisibly return the input

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:

    expect_length <- function(object, n) {
      act <- quasi_label(rlang::enquo(object), arg = "object")

      act$n <- length(act$val)
      if (act$n == n) {
        succeed()
        return(invisible(act$val))
      }

      message <- sprintf("%s has length %i, not length %i.", act$lab, act$n, n)
      fail(message)
    }

## Testing your expectations

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))
    })