Another requirement of developing functions for the package is that they have some testing built in. Testing is the process of specifying the expected resuts of your function given a fixed input. Once a proper set of tests for a function has been built you can modify the function and the tests will inform you if the behaviour has changed. This can be extremely helpful when developing a colleciton of functions which are interdependant, as it will alert you to when the behavior of a function changes during development even if you were not currently changing that function.
The standard for test frameworks in R is testthat
which
is an R package that comes with a set of functions for constructing
tests and integrates with devtools
so that the tests are
run every time the package is built. Tests must be written into files
with names beginning with test_
which are then placed in
the tests/testthat
folder relative to the package root. A
typical convention is to have one test file per function (as with
function writing).
This document will not cover the principles of unit-testing here, there are many resources for this - (see here) - a simple example test is shown below.
context("Must calculate the cumulative hazard over [0, tmax] for piecewise constant model")
test_that("`pwe_H`", {
# Generate output - function vectorises with 'time' arg, so we use a vector
# of length > 1 to test this functionality
output <- pwe_H(time = 1:10, cut.pts = c(0, 3, 10, Inf), haz.rates = 1:5)
# check class
expect_is(output, "numeric")
# check values
expect_equal(output, c(2, 4, 6, 9, 12, 15, 18, 21, 24, 27.0001144483108))
})
The call to context()
lets us preface the following test
with a statement of the functional requirement. This is helpful when the
automated testing happens as this title will help direct us to any
failing test in the console output. It’s also good practice to
explicitly state the functional requirement so any other developers
reading these tests are clear on what you intended the function to do
(and not do). After context()
there is a
test_that("", {})
chunk where we lay out our expectations
of the function. Schematically we generate a fixed output for a fixed
input passed through the function we are testing, and we then check our
expectations on that output such that they satisfy our requirement laid
out above in context()
.
Tests can be run interactively using the
devtools::test()
function.