Tests are your early warning system if something starts to go awry in your project. The more complex your project, the more important it becomes to have a robust set of tests. This is especially true if you have collaborators.
While tests don’t directly benefit the users of your package, they do benefit you the authors. Say you have designed a function, and it is working as you intended. It is possible that someone else on your team could make a change that unintentionally breaks the function. If you created a test to verity your intended output, your test can now tell your collaborator that something has gone wrong. If the test dosn’t exist, you’re relying on some human down the line noticing the mistake.
Today we will be creating a simple test for out comma_split()
function. I’ve provided a copy of it below. Create a new empty package
or include it in your final project package by creating a new R script
file named comma_split.R
inside the ./R/
directory of your package
project. Paste our comma_split()
function inside and save it.
comma_split = function(vector_to_split, possible_columns){
# make a base dataframe with rows for each of our cases.
output = data.frame(
"id" = 1:length(vector_to_split)
)
# iterate through all options and create a column with NAs for it
for(option in possible_columns){
# make a new column with a character version of each possible option.
output[, as.character(option)] = NA
}
# fill output df
for(option in possible_columns){
# fill dataframe iterativly.
output[ , option] = grepl(option, vector_to_split, ignore.case = TRUE)
}
# clear all know options
for(option in possible_columns){
# remove all known options
vector_to_split = gsub(pattern = option, vector_to_split, replacement = "", ignore.case = TRUE)
}
# clear commas and whitespace
vector_to_split = gsub(pattern = ",", vector_to_split, replacement = "", ignore.case = TRUE)
vector_to_split = trimws(vector_to_split)
# Fill in "other"
output$other = vector_to_split
# Turn blanks into NAs
output[output$other == "" & !is.na(output$other), "other"] = NA
# return output
return(output)
}
Once you have comma_split.R
saved, open it in R Studio. With it active
in the viewer pane, enter the following into the console:
usethis::use_test()
This will create a tests/testthat/
directory in your project, and
create a test-comma_split.R
file. This is where the tests related to
our function should live. Open that file if it is not open already.
Inside the file, you should see some boilerplate test code. This will be
run whenever you try to test or check your package. You can give it a go
by running devtools::test()
, or by clicking the Test
button in the
build tab.
Let’s break down the example. The test_that()
function accepts a
description in the first argument position, and then code within curly
braces. This is a single test. Inside of that test, you can write some
code, and describe what you expect that code to return using the
expect_XXXX()
family of functions.
test_that("multiplication works", {
expect_equal(2 * 2, 4)
})
There are a number of different expect function you can use (full list here). All of them generally expect some code in the first position, and then what you expect that code to output in the second. If the code does not produce that output, that part of the test has failed, and you will be alerted.
Try modifying the boilerplate test so that we expect the output to be 5.
Run devtools::test()
. Interpret the results.
Now that we understand how tests work, we can start creating our own. We
will now modify the contents of tests/testthat/test-comma_split.R
to
create an applicable test for our comma_split()
function.
Create a simple test for our comma_split()
function to assure it is
behaving as intended. You may want to look over the various forms of
expect_XXXX()
functions here. Once you have created
your test, make sure it works by using devtools::test()
.
test_that(“Are there any commas in the results?”, {
test_vec = c(“Mon, Tues, Wed”) possible = c(“Mon”, “Tues”, “Wed”, “Thurs”, “Fri”)
out = comma_split(test_vec, possible)
expect_false(all(grepl(“,”, out)))
})
Once you have made your test and it works as intended, try running a
check on your entire package using either devtools::check()
or the
button under the Build tab in the upper right pane.
If you have been using the proper package::function()
notation for
your packages, you will get an error along the lines of:
<packageNotFoundError/error/condition>
Error in `loadNamespace(x)`: there is no package called 'stringr'
Backtrace:
▆
1. ├─mypackage::comma_split(test_vec, possible) at test-comma_split.R:6:2
2. │ └─base::print(stringr::str_count(string = vector_to_split, pattern = ","))
3. └─base::loadNamespace(x)
4. └─base::withRestarts(stop(cond), retry_loadNamespace = function() NULL)
5. └─base (local) withOneRestart(expr, restarts[[1L]])
6. └─base (local) doWithOneRestart(return(expr), restart)
To resolve this, simply enter usethis::use_package("PACKAGE NAME")
to
add the package as a dependency to your package, so R will know it is
required when some loads your pacakge using library(YOUR PACKAGE)
.