Assertive Programming in R. Your code should work as intended or... | by Denis Gontcharov | Towards Data Science

You might be familiar with unit testing using the testthat package. The goal of unit tests is to check if your function is developed correctly. An assertion checks if your function is used correctly.
Unit tests are meant for the developer and are executed on command. Assertions are meant for the user and are executed at every function call.
A run-time test, commonly known as an assertion, is a tiny piece of code that checks a condition and fails if the condition isn’t met. Assertions enable your code to fail fast — and that’s a good thing. When something is off we don’t want our program to continue until the error compounds and reveals itself down the line. On the contrary, we want the program to fail immediately at the source of the error with a clear and precise error message.
This goal of this article is to make your R functions fail fast. Assuming your function has no bugs or side-effects, the only way an error can creep into your function is through its input. In this article, you will learn how to use assertions to recognize bad input and warn the user (including future you).
There are two ways for input to be bad:
The function input contains errors. Some errors are spotted by R: from impossible or missing values to inconsistent use of commas and points in decimal numbers. Other errors are more insidious. Think about invalid credit card numbers or non-existing postal codes and bad email formats.You developed your function with a particular use case in mind. You can’t imagine all the ways a user will try to use your code. He or she may use your function with technically correct input but not in the intended way. Have you tried sorting a list with sort.list(list(2, 1, 3))?
The function input contains errors. Some errors are spotted by R: from impossible or missing values to inconsistent use of commas and points in decimal numbers. Other errors are more insidious. Think about invalid credit card numbers or non-existing postal codes and bad email formats.
You developed your function with a particular use case in mind. You can’t imagine all the ways a user will try to use your code. He or she may use your function with technically correct input but not in the intended way. Have you tried sorting a list with sort.list(list(2, 1, 3))?
R has several packages for writing assertions. This article advocates the R package assertive for three reasons:
the package provides lots of functions for many different casesits functions are very easy to read
the package provides lots of functions for many different cases
its functions are very easy to read
assert_all_are_positive(c(1, -2, 3))
3. and provide highly informative error messages
Error: is_positive : c(1, -2, 3) contains non-positive values.There was 1 failure: Position Value Cause1 2 -2 too low
Install the package directly from CRAN and load it into your R session:
install.packages("assertive")
library(assertive)
Imagine you wrote a function that sums the elements of a numeric vector:
add_numbers <- function(numbers) {  result <- sum(numbers)  return(result)}
The values of the vector are allowed to be anything as long as they are numbers. Let’s write an assertion at the beginning of the function to check our input vector:
add_numbers <- function(numbers) {  assert_is_numeric(numbers)  result <- sum(numbers)  return(result)}add_numbers(c(1, 2, 2, 3)) # pass
Note that assertion is written inside the function. This means it’s always there, waiting to be executed each time the function is called. Checking an assertion only takes a couple of milliseconds which is fine for all but the most performance-critical applications.
Imagine you modified your function to expect a vector of unique values. Now you need two assertions:
one to check that the vector is numericanother one to check that the vector has no duplicate values
one to check that the vector is numeric
another one to check that the vector has no duplicate values
You can keep your code readable by chaining two or more assertions with the forward pipe %>% from the magrittr package:
library(magrittr)add_unique_numbers <- function(input_vector) {  input_vector %>%     assert_is_numeric %>%    assert_has_no_duplicates  result <- sum(input_vector)  return(result)}add_unique_numbers(c(1.5, 2, 2, 3)) # fail
There are a lot of assertive functions for a wide range of cases. It’s not practical to memorize them all. Just search the package documentation on CRAN to find a function that suits your particular need.
Let’s try this with an exercise. The function below expects a vector of percentage values, e.g.percentages <- c(64, 55, 97, 85) and computes the mean percentage. Can you find an appropriate assertion in the assertive package?
calculate_mean_percentage <- function(percentages) {    # ... your assertion goes here     result <- mean(percentages)    return(result)}
Tip: write ls("package:assertive", pattern = "percent") in your R console to search for functions in the assertive whose name matches “percent”.
You may start to see a pattern here. All assertions are composed of three parts:
they always begin with assert_followed by the predicate is_ or has_and end with an expectation, e.g. numeric
they always begin with assert_
followed by the predicate is_ or has_
and end with an expectation, e.g. numeric
The predicate is_ changes to all_are_ or any_are_ in when the individual elements of an object are checked instead of the object itself. For example, the assertion below checks if all numbers passed to the function are whole numbers:
add_whole_numbers <- function(whole_numbers) {  assert_all_are_whole_numbers(whole_numbers)  result <- sum(whole_numbers)  return(result)}add_unique_numbers(c(1.5, 2, 2, 3)) # fail
So far we’ve only dealt with numbers. But the assertive package contains assertions for all kind of cases. To illustrate, we’ll finish our discussion with some examples to check text, dates and even the host operating system.
Does your function expect a vector of personal names? Check for missing or empty strings:
assert_all_are_non_missing_nor_empty_character()
Have a function that only works on Windows? Then make sure the user is running Windows:
assert_is_windows()
Does your function expect a person’s date of birth? Make sure it’s in the past:
assert_is_in_past()
In this article, you learned how to write assertions using the assertive package. Thanks to assertions your functions will do what they are supposed to do or fail fast with a clear error message.
Cotton, Richard. (2017). Testing R code (1st Edition). Chapman & Hall.
Hunt, Andrew. Thomas, Dave. (1999). The Pragmatic Programmer (25th Printing, February 2010). Addison Wesley Longman, Inc.