assertive
An R package that provides readable check functions to ensure code integrity.
There are times when it is a good idea to check the state of your variables, to ensure that they have the properties that you think they have. For example, if you have a count variable, you might want to check that it is numeric, that all the values are non-negative, and that all the values are whole numbers.
assertive provides lots of functions ("predicates" and "assertions") to provide such checks. It is designed to make your code very easy to read, and to provide highly informative error messages.
Installation
To install the stable version, type:
install.packages("assertive")
To install the development version, you first need the devtools package.
install.packages("devtools")
Then you can install the assertive package using
library(devtools)
install_bitbucket("richierocks/assertive")
How to use the package
assertive contains lots of assert functions ("assertions") that throw errors if conditions aren't met. They are very useful for checking user input to your functions.
For example,
f <- function(x)
{
assert_is_not_null(x)
x + 1
}
f(1)
## [1] 2
f(NULL)
## Error in f(NULL) : x is NULL.
(You can think of the assert functions as more specific versions of
base::stopifnot
that make your code easier to read and give more informative
error messages.)
Each assert function has a corresponding is function (a "predicate"). In
this case, is_not_null
is a wrapper to base-R's !is.null
, that gives a more
informative error message on failure (in an attribute named cause
).
is_not_null(1)
## [1] TRUE
is_not_null(NULL)
## [1] FALSE
## Cause of failure: NULL is NULL.
Many of the is functions are wrappers to base functions. They all return
causes of failure, and they have consistent naming, beginning is_
or has_
(so base::interactive
becomes is_interactive
, for example.)
Vectorised is functions
Some is functions return a logical vector rather than a single value. In this case the input values are returned in the names to make it easier to see which values succeeded/failed, and the cause attribute is also vectorised.
For example,
is_positive(c(1, 0, -1, NA))
## There were 3 failures:
## Position Value Cause
## 1 2 0 too low
## 2 3 -1 too low
## 3 4 <NA> missing
Using unclass
, you can see that this is just a logical vector, with a cause
attribute.
unclass(is_positive(c(1, 0, -1, NA)))
## 1 0 -1 <NA>
## TRUE FALSE FALSE NA
## attr(,"cause")
## [1] too low too low missing
There are two corresponding assert functions for these vectorised is functions.
assert_any_are_positive(c(1, 0, -1, NA)) # test passed since 1 is positive
assert_all_are_positive(c(1, 0, -1, NA))
## Error: c(1, 0, -1, NA) contains non-positive values.
## There were 3 failures:
## Position Value Cause
## 1 2 0 too low
## 2 3 -1 too low
## 3 4 <NA> missing
Can't I just use testthat?
testthat is an excellent package for writing unit tests, and I recommend that you use it. Unit tests are a form of development-time testing. That is, you write the tests while you develop your code in order to check that you haven't made any mistakes.
assertive, and assertions in general, are for run-time testing. That is, you include them in your code to check that the user hasn't made a mistake while running your code.
The virtual package system
assertive is a virtual package; it does not contain any functions, but merely reexports them from lower-level packages. For a complete reference, see the individual package pages.
assertive.base contains
the core functionality. For example, is_true
checks when inputs return
TRUE
. It also contains some utility functions, such as use_first
, which
returns the first value of a vector, warning you if it was longer than length
one.
assertive.properties
contains checks on properties of variables. For example, is_scalar
checks
for values that have length one, and has_duplicates
checks for the presence of
duplicate values.
assertive.types contains
checks for types of variables. For example, is_character
wraps the base
is.character
, while is_a_string
combines that check with is_scalar
to
check for single strings.
assertive.numbers
contains checks for numbers. For example, is_in_range
checks if a number is
in a numeric range.
assertive.strings
contains checks for strings. For example, is_an_empty_string
checks if
a character vector contains a single empty string.
assertive.datetimes
contains checks for dates and times. For example, is_in_past
checks if a
Date
or POSIXt
obejct is in the past.
assertive.files contains
checks for files and connections. For example, is_readable_file
checks if
a path points to a file that R has permission to read, and is_file_connection
check if an object is a file connection.
assertive.sets contains
checks for sets. For example, is_subset
checks if a vector is a subset of
another vector.
assertive.matrices
contains checks for matrices. For example, is_symmetric_matrix
checks if
a matrix is symmetric.
assertive.models
contains checks for models. For example, is_empty_model
checks if a model
is the empty model (no factors).
assertive.data contains
checks for complex data types. For example, is_credit_card_number
checks a
character vector for valid credit card numbers.
assertive.data.uk
contains checks for UK-specific complex data types. For example,
is_uk_postcode
checks a character vector for valid UK postcodes.
assertive.data.us
contains checks for US-specific complex data types. For example,
is_us_telephone_number
checks a character vector for valid US telephone
numbers.
assertive.reflection
contains checks on the state of R. For example, is_solaris
tests for that
operating system, and is_rstudio
tests for that IDE.
assertive.code contains
checks for code. For example, is_valid_variable_name
checks whether a
character vector contains valid variable names.
I hate this; what's the alternative?
There are at least five other R packages for doing assertions. In alphabetical order of package:
- Tony Fishettti's assertr
- Hadley Wickham's assertthat
- Michel Lang's checkmate
- Stefan Bache's ensurer
- Gaston Sanchez's tester
I want to know more
There are several vignettes with more details on how to use the package, including case studies and exercises. Find them using
browseVignettes()
I want to help
The assertive packages are in the process of being translated into many languages. If you speak a language other than English, and have an hour or two spare, your translation skills would be appreciated.
assertive is also currently lacking assertions for time series, spatial data, personal data for countries other than the UK and US, and industry-sector-specific data. If you want to contribute a package for these data types, let me know and I can talk you through how to do it.