Concise TAP-compliant unit testing package.
Authored unit tests can be run using R CMD check
with minimal implementation overhead.
If you want more features there are other unit testing packages (see 'See Also').
You don't need to be writing a package to use unittest. Suppose you have a file biggest.R
with the following function
biggest <- function(x,y) {max(c(x,y))}
To test this create a file called test_biggest.R
in the same directory containing
library(unittest, quietly = TRUE)source('biggest.R')
ok(biggest(3,4) == 4, "two numbers") ok(biggest(c(5,3),c(3,4)) == 5, "two vectors")
Now in an R
session source()
the test file
source('test_biggest.R')
and you will see output like this
ok - two numbers ok - two vectors
and that's it.
Now each time you edit biggest.R
re-sourcing test_biggest.R
reloads your function and runs your unit tests.
First, add unittest
to Suggests:
in the package DESCRIPTION
file.
Suggests: unittest
Then create a directory called tests
in your package source, alongside your R
directory.
Any .R
file in the tests
directory will be run by R CMD check
. The unittest
package
“knows” that it is being run by CMD check
and so at the end of the tests will produce a
summary and throw an error if any tests fail; throwing an error will in turn cause CMD check
to
report the error and fail the check
.
Assuming your package contains (and exports
) the biggest()
function from
‘Getting started’, we could add a tests/test_biggest.R
that contains
library(mypackage) library(unittest, quietly = TRUE)ok(biggest(3,4) == 4, "two numbers") ok(biggest(c(5,3),c(3,4)) == 5, "two vectors")
and that's it. R CMD check
will run the tests and fail if any of the tests fail.
When dealing with many unit tests in one file it can be useful to group related unit tests.
The ok_group
function is used like this
ok_group("Test addition", { ok(1 + 1 == 2, "Can add 1") ok(1 + 3 == 4, "Can add 3") }) ok_group("Test subtraction", { ok(1 - 1 == 0, "Can subtract 1") ok(1 - 3 == -2, "Can subtract 3") })
which will produce output like this
# Test addition ok - Can add 1 ok - Can add 3 # Test subtraction ok - Can subtract 1 ok - Can subtract 3
Define a helper function test_for_error
that uses tryCatch
. If your test
results in an error that does not match, then the test fails and the actual error will
be included in your test results.
test_for_error <- function(code, expected_regexp) { tryCatch({ code return("No error returned") }, error = function(e) { if(grepl(expected_regexp, e$message)) return(TRUE) return(c(e$message, "Did not match:-", expected_regexp)) } ) }
add_four <- function(x) { if( ! is.numeric(x) ) stop("x must be numeric") return( x+4 ) }
ok(test_for_error(add_four("a"), "must be numeric"), "add_four() argument not numeric correctly throws an error") this will result in the output
ok - add_four() argument not numeric correctly throws an error
If you have some unit tests which require access to un-exported functions,
or un-exported S3 methods, you can use local
.
local({ ok(internal_function() == 3) ok(another_internal_function() == 4) ok(final_internal_function() == 5) }, asNamespace('mypackage'))
Use all.equal(...)
a <- c(1,2,3) b <- 1:3 ok(all.equal(a,b), "a and b are equal")
Alternatively, the following helper function will give coloured output showing what's different
cmp <- function(a, b) { if(identical(all.equal(a,b), TRUE)) return(TRUE)
if (file.exists(Sys.which('git'))) { totmp <- function(x) { f <- tempfile(pattern = "str.") capture.output(str(x, vec.len = 1000, digits.d = 5, nchar.max = 1000), file = f) return(f) }
return(suppressWarnings(system2( Sys.which('git'), c("diff", "--no-index", "--color-words", totmp(a), totmp(b)), input = "", stdout = TRUE, stderr = TRUE))) }
return(c( capture.output(str(a)), "... does not equal...", capture.output(str(b)) )) } To see the coloured output, try the following:
ok(cmp(1:3, 1:8))
The unittest
package provides two functions, ok
and ok_group
.
The ok
function prints ok
when the expression provided evaluates to TRUE
and prints not ok
if the expression evaluates to anything else or results in a runtime error;
this is the TAP
format (http://testanything.org/) for reporting test results.
The ok_group
function is a convenience function for grouping related unit tests
and produces TAP
compliant comments in the output to separate the unit test groups.
A unit test summary is produced at the end of a session when a set of unit tests are run in non-interactive
mode, for example when the unit tests are run using Rscript
or by R CMD check
. For
using with R CMD check
, see ‘I'm writing a package, how do I put tests in it?’.
For a list of all documentation use library(help="unittest")
.
Inspired by Perl's Test::Simple (http://search.cpan.org/perldoc?Test::Simple).