Unit Testing Using Packages testthat and devtools
category: developmenttesting
date: July 09, 2016
R unit testing using packages testthat and devtools
Unit testing in R can be performed using the R packages testthat and devtools. The packages testthat and devtools provide functions for implementing unit testing in R.
The package testthat provides functions for implementing unit tests for other R functions. The unit tests are stored in test files. It’s convenient to create separate test files for each tested R function, and name the test files test-function-name.R
. The test files can be executed any time the user needs to verify that her R functions are working properly.
The package devtools provides functions for executing a whole set of unit tests stored in test files. This is especially convenient for testing a whole package of R functions.
You can read more about unit testing of R packages on Hadley Wickham’s website: unit testing of R packages.
Setting up a package for unit testing using devtools
The function use_testthat() from package devtools sets up a package for unit testing (in this example the package rutils
stored in "C:/Develop/R/rutils"
):
library(devtools)
devtools::use_testthat("C:/Develop/R/rutils")
The function use_testthat() from package devtools performs the following operations:
-
creates the directories
./tests
and./tests/testthat
, (the directory./tests/testthat
is designed to store the test files namedtest*.R
), -
creates a file
./tests/testthat.R
for running all the test files, -
adds a field
Suggests: testthat
in the./DESCRIPTION
file.
After the package is set up for unit testing, the user can add test files to the
directory ./tests/testthat
, using the package testthat.
Writing unit tests using package testthat
The package testthat contains functions for implementing unit tests.
The functions expect_*() perform the actual unit tests by comparing the values returned by functions with the values expected by the user. The expect_*() functions use the base functions grep(), identical(), and all.equal() to determine equality.
The function test_that() is used as a wrapper around the functions expect_*(), to be able to handle the errors they produce when an unexpected value is returned.
The function context() defines a set of tests that test related functionality.
The package testthat contains functions for implementing unit tests:
-
expect_match(): tests whether its first argument matches a regular expression provided by the second argument,
-
expect_equal(): tests whether its first argument is equal to the second argument, using the function all.equal(),
-
expect_true() and expect_false(): test whether their first argument is TRUE or FALSE,
-
test_that(): performs one or more tests using calls to functions expect_*(),
-
context(): defines a set of tests that test related functionality,
Unit tests are organized as a set of calls to the functions expect_*(), each wrapped by the function test_that(). Unit tests related to a similar functionality or function can be collected in the same file under a single context().
Below are unit test code examples designed to demonstrate the functions from package testthat. The test code examples are collected under a single context().
-
The function expect_match() tests whether its first argument matches a regular expression provided by the second argument.
If there is a match, then expect_match() invisibly returns its first argument, and if there is no match, then it produces an error without returning anything. For example,
expect_match("character", "char")
invisibly returns"character"
, whileexpect_match("integer", "char")
produces an error without returning anything.The function expect_match(), together with functions typeof() and class(), can be used to test for the type or class of an object.
library(testthat)
# define context for demos of function expect_match()
context("demo of expect_match")
# expect_match() returns its first argument invisibly
test_result <- expect_match("character", "char")
# expect_match() produces an error
test_result <- expect_match("integer", "char")
# test for object type returns "character" invisibly
test_result <- expect_match(typeof("a"), "char")
# test for object type produces an error
test_result <- expect_match(typeof(11), "char")
# test for object class returns "matrix" invisibly
test_result <- expect_match(class(matrix(1:9, nr=3)), "matrix")
# test for object class produces an error
test_result <- expect_match(class(1:9), "matrix")
-
The function expect_equal() tests whether its first argument is equal to its second argument, by calling the function all.equal() to determine equality.
If the arguments are equal, then expect_equal() invisibly returns its first argument, but if they are not equal, then it produces an error without returning anything.
library(testthat)
# define context for demos of function expect_equal()
context("demo of expect_equal")
# expect_equal() returns its first argument invisibly
test_result <- expect_equal(0.3/3, 0.1)
# expect_equal() produces an error
test_result <- expect_equal(0.3/3, 1.0)
-
The function test_that() performs one or more tests using calls to functions expect_*(), and handles any errors they produce when an unexpected value is returned.
The first argument is a string representing the test name. The second argument is an expression containing one or more calls to functions expect_*().
The function test_that() is used as a wrapper around the functions expect_*(). test_that() invisibly returns TRUE if its second argument doesn’t produce any errors, and produces an error without returning anything, if any of the functions expect_*() produce an error.
library(testthat)
# define context for demos of function test_that()
context("demo of test_that")
# produces error and handles it using test_that()
test_result <- test_that("first test", stop("whatever"))
# passes the test and invisibly returns TRUE
test_result <- test_that("second test", expect_true(1>0))
test_result <- test_that("third test", {
expect_true(1>0)
expect_match(typeof("a"), "char")
})
-
We can now apply the above functions from the package testthat to write some unit test code.
Below is unit test code for the function na_me() from package rutils:
library(testthat)
library(rutils)
# define context for tests of function na_me()
context("test na_me")
# test the type returned by na_me()
test_that("type must be character", {
expect_match(typeof(na_me(env_etf$VTI)), "character")
})
# test the value returned by na_me()
test_that("value must be VTI", {
expect_match(na_me(env_etf$VTI), "VTI")
})
- Below is unit test code for the function end_points() from package rutils:
library(testthat)
library(rutils)
# define context for tests of function end_points()
context("test end_points")
# test the type returned by end_points()
test_that("end_points must be integer", {
expect_match(typeof(end_points(env_etf$VTI)), "integer")
})
# test the value returned by end_points()
test_that("end_points value", expect_false(last(end_points(env_etf$VTI)) > NROW(env_etf$VTI)))
Applying unit tests using package devtools
Once the directory ./tests/testthat
has been populated with test files named test*.R
, the user can execute all the tests by calling the function test() from package devtools:
library(devtools)
library(rutils)
devtools::test("C:/Develop/R/rutils")