Create Custom Assertions

Getting Started

Do you have a custom assertion that you want to use repeatedly in your code? Then create your own assertion functions with the assert_create() function!

Lets start by recreating the assert_numeric() assertion using assert_create():

# Load library
library(assertions)

# Create a function that asserts input is numeric
assert_numeric_2 <- assert_create(
  func = is.numeric, # Returns TRUE/FALSE for assertion PASS/FAIL
  default_error_msg = "'{arg_name}' must be of type numeric, not {class(arg_value)}"
  )

# Assertion passes if input is numeric
assert_numeric_2(123)

# But throws the expected error if input is not numeric
assert_numeric_2("abc")

To create a custom assertion function, you need to supply two arguments to assert_create():

func: a function that take an object to assert and returns TRUE or FALSE depending on whether the assertion should pass or fail.

default_error_msg: a character string providing an error message in case the assertion fails. This string can include special termss such as:

  1. {arg_name} to refer to the name of the variable being checked
  2. {arg_value} to refer to the value of the variable.
  3. {code_to_evaluate} to evaluate code within the error message. Customise ‘code_to_evaluate’. e.g {class(arg_name)}
  4. {.strong bold_text} to perform inline formatting. Customise ‘bold_text

Advanced Assertions

Problem:

Sometimes it is necessary to perform several assertions on a single object, and return error messages specific to the mode of failure. In these cases, it can be useful to chain together a series of different assertions on the object.

Solution:

The assert_create_chain() function allows you to combine multiple assertion functions created with assert_create() into a single assertion. Each of the wrapped assertions are evaluated in the order they are supplied, and if any of the assertions fail, the appropriate error message is returned.

Example

Here’s an example of how to use assert_create_chain() to create an assertion function that asserts input is a string by individually asserting that

  1. Input is a ‘character’ type

  2. Input length is 1

assert_string <- assert_create_chain(
  assert_create(is.character, '{arg_name} must be a character, not {class(arg_value)}'),
  assert_create(function(s){ length(s)==1 }, '{arg_name} must be length 1, not {arg_value}')
)

# Assert String
assert_string("String")

assert_string(3)
# Output: Error: '3' must be a character

More Advanced Assertions

Problem:

We often need error messages to vary significantly based on the input.

Solution

In such cases, it is more convenient to define the error messages in the function you pass to func. How does this work?

In our call to assert_create, instead of defining a default_error_msg, we can design func to return a string when the assertion should fail. This string will become the error message. By returning different strings upon different failure conditions, we can produce very diverse error messages very easily.

Example

Here’s a recreation of the example above, using a func that supplies strings to indicate assertion failure

# Define Function
is_a_string <- function(object){
 if(!is.character(object))
   return("{arg_name} must be a character, not {class(arg_value)}")
  
  if(length(object) != 1){
    return("{arg_name} must be length 1, not {length(object)}")
  }
  
  return(TRUE)
}

# Create Assertion
assert_is_string <- assert_create(
 is_a_string
)

# Test assertion works
assert_is_string("String")

assert_is_string(3)
# 3 must be a character, not numeric

assert_is_string(c("A", "B"))

Additional Notes

Note that in your error strings can use the special terms such as {arg_name}, but you will NOT have access to the first argument using its original name (e.g. {object}, in the example above). This is because assert_create changes this first arguments name.

Values of all other arguments can be referred to in string using {name_of_nonfirst_argument}