Testing Your R Package
Robust testing is a cornerstone of developing high-quality R packages. It ensures your code functions as expected, prevents regressions, and builds confidence for users. This module will guide you through the essential aspects of testing your R package, focusing on the
testthat
Why Test Your R Package?
Testing serves multiple critical purposes in package development:
- Ensuring Correctness: Verifies that your functions produce the expected outputs for given inputs.
- Preventing Regressions: Catches unintended side effects when you modify or add new code.
- Improving Code Quality: Encourages writing modular, well-defined, and maintainable code.
- Facilitating Collaboration: Provides a clear contract for how your package should behave, making it easier for others to contribute or use.
- Building User Trust: Demonstrates that your package has undergone rigorous validation.
Introduction to `testthat`
testthat
`testthat` uses a clear, readable syntax for writing tests and assertions.
Tests are typically written in files within a tests/testthat/
directory. Each test file contains one or more test cases, often grouped by the function or feature they are testing. testthat
provides functions like test_that()
, expect_equal()
, expect_true()
, and expect_error()
to structure and execute these tests.
A typical test file might look like this:
# tests/testthat/test-my_function.R
library(testthat)
library(your_package_name)
test_that("my_function works with positive numbers", {
expect_equal(my_function(2, 3), 5)
})
test_that("my_function handles zero input", {
expect_equal(my_function(0, 5), 5)
})
test_that("my_function throws an error for negative input", {
expect_error(my_function(-1, 5))
})
When you run devtools::test()
, testthat
discovers and executes these tests, reporting any failures or errors.
Key `testthat` Assertions
Assertion Function | Purpose | Example Usage |
---|---|---|
expect_equal(object, expected) | Checks if two objects are identical. | expect_equal(sum(1:5), 15) |
expect_identical(object, expected) | Checks if two objects are identical in both value and type. | expect_identical(list(1, 'a'), list(1, 'a')) |
expect_true(condition) | Checks if a condition evaluates to TRUE. | expect_true(is.numeric(my_variable)) |
expect_false(condition) | Checks if a condition evaluates to FALSE. | expect_false(is.null(my_variable)) |
expect_error(expression, regexp = NULL) | Checks if an expression throws an error. Optionally checks the error message. | expect_error(stop('Error message'), 'Error message') |
expect_warning(expression, regexp = NULL) | Checks if an expression throws a warning. Optionally checks the warning message. | expect_warning(warning('Warning message'), 'Warning message') |
expect_output(expression, regexp = NULL) | Checks the standard output of an expression. | expect_output(print('Hello'), 'Hello') |
Running Your Tests
The most convenient way to run your package tests is using the
devtools
devtools
install.packages('devtools')
- : Runs all tests in thecodedevtools::test()directory.codetests/testthat/
- : Runs only tests in a specific file.codedevtools::test(filter = "test-my_function.R")
- : Runs tests that contain the string "my_function" in their name.codedevtools::test(filter = "my_function")
It's a good practice to run your tests frequently, ideally before committing changes and as part of your continuous integration (CI) pipeline.
Best Practices for Writing Tests
- Test one thing at a time: Each test should focus on a single behavior or condition.
- Make tests independent: Tests should not rely on the outcome of other tests.
- Test edge cases: Consider boundary conditions, invalid inputs, and error scenarios.
- Keep tests fast: Slow tests can discourage frequent execution.
- Test public API: Focus on testing the functions that users will interact with directly.
- Use descriptive test names: Clearly indicate what each test is verifying.
Beyond Unit Tests: Integration and Examples
While unit tests are crucial, consider other forms of testing:
- Integration Tests: Verify that different parts of your package work together correctly.
- Example Tests: can also run the examples provided in your package's documentation (`codetestthat
# tests/testthat/test-examples.Rlibrary(testthat)library(your_package_name)context("Package Examples")example("my_function", package = "your_package_name", run_dontrun = TRUE)
This ensures your documentation examples are up-to-date and functional.
- Vignettes: While not strictly tests, well-written vignettes serve as extended examples and can be checked for correctness.
The testthat
framework provides a structured way to write and run tests. A test file typically begins with loading the necessary libraries and then uses test_that()
to define individual test cases. Inside each test_that()
block, assertion functions like expect_equal()
are used to compare actual results with expected outcomes. This systematic approach helps in identifying and fixing bugs efficiently.
Text-based content
Library pages focus on text content
Learning Resources
The official documentation for the testthat package, providing comprehensive guides and function references.
A chapter from the 'R Packages' book, offering a practical and in-depth explanation of testing R packages with testthat.
A blog post that provides practical advice and best practices for writing effective unit tests in R.
A video tutorial demonstrating how to set up and write tests for an R package using testthat.
Information on package testing within the R-hub ecosystem, which is crucial for CRAN submissions.
The introductory vignette for testthat, explaining its core concepts and usage.
A blog post from R-Ladies that covers the basics of testing R code, including an introduction to testthat.
A reference page detailing the various assertion functions available in testthat for checking conditions.
While not solely about testing, this guide covers the broader context of building R packages, including testing as a critical step.
A tutorial that explains the importance of testing in R programming and introduces basic testing concepts.