# add.R
add <- function(a, b) {
a + b
}
library(testthat)
test_that("add works correctly", {
expect_equal(add(2, 3), 5)
expect_equal(add(-1, 1), 0)
})Test passed with 2 successes 🎉.
This site contains materials for the testing module on HDR UK’s RSE001 Research Software Engineering training course. It was developed as part of the STARS project.
The standard and recommended way to run tests in Python is from the terminal using pytest.
This approach works when you have:
Your test files need to import the functions, classes, or modules you want to test. This can be done in several ways depending on your project structure:
test_ naming conventionPytest automatically discovers and runs tests when your test files start with test_. It is also typical (but not mandatory) to store test files in a folder called tests/. For example:
tests/
├── test_back.py
├── test_functional.py
└── test_unit.py
Once your project follows these conventions, you can run tests from a terminal, PowerShell, or command prompt (depending on your OS). Common commands include:
The standard and recommended way to run tests in R is from the R console, running them either via devtools or directly with testthat.
When your code is structured as a package, your project might look like:
project/
├── data/
│ └── patient_data.csv
├── pyproject.toml
├── README.md
├── src/
│ └── waitingtimes/
│ ├── __init__.py
│ └── patient_analysis.py
└── tests/
├── test_intro_simple.py
└── ...
The video below demonstrates running tests on a package structured this way. In the video we:
tree to display the file structure (matches that shown above).conda activate hdruk_tests (which includes pytest).pip install -e ..conda list, confirming our waitingtimes package appears.nano tests/test_intro_simple.py) (matches the one from the previous page on writing basic tests).pytest tests/test_intro_simple.py - and it passes! 🎉In an R package, tests will be automatically discovered and run if they follow the conventions of being:
tests/testthat/.test_ or test-.For example, your project might look like:
project/
├── inst/
│ └── extdata/
│ └── patient_data.csv
├── man/
│ └── ...
├── R/
│ └── patient_analysis.R
├── tests/
│ └── testthat/
│ ├── test_intro_simple.R
│ └── ...
├── .Rbuildignore
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
└── README.md
You can either run the tests with devtools or testthat - common commands include:
In the video below, we have an example of a research project structured as an our R project.
Project '~/Documents/stars/hdruk_tests' loaded. [renv 1.1.5]).test_intro_simple.R).devtools::test() and can see that all tests pass.If you’re unfamiliar with how to set up a package, check out our tutorial on packaging your research project.
You don’t need a full package to use pytest. When your code exists in .py files but isn’t packaged, you can still run tests from the terminal. Your project might, for example, look like:
project/
├── patient_analysis.py
└── test_intro_simple.py
In the video below, we:
tree to display the file structure - there is just a .py file with the summary_stats() function and test_intro_simple.py file alongside it.nano test_intro_simple.py, showing it imports locally since files are in the same folder.pytest, since the file follows the test_ naming pattern.You don’t need a full package to use testthat. When your code exists in .R files but isn’t packaged, you can still run tests from the R console. You project might, for example, look like:
project/
├── patient_analysis.R
└── test_intro_simple.R
As your test follows of the convention of starting test_, it can still be detected by testthat and run by testthat:test_dir() or testthat::test_file().
Note: You cannot use
devtools::test()with this approach.
In the video below, we:
test_intro_simple.R file, showing how it imports the local function.testthat::test_dir("."), which finds and runs our test.While running tests from the command line is the recommended approach, there are alternative tools for specific use cases.
testbook allows you to test code within Jupyter notebooks:
ipytest lets you run pytest directly inside Jupyter notebook cells:
While running tests from the R console is the recommended approach, it is also possible to put tests directly in the same .R file as your code and just run the script.
For example:
Running tests from the terminal with code organised into separate files is the preferred approach because:
Test code is separate from analysis code, making both easier to understand and maintain.
Tests can be run automatically in continuous integration (CI) pipelines, before commits, or on schedule.
As your project grows, you can easily add more test files and organise them logically without cluttering notebooks or scripts.