9 Using tests with testthat
As we discussed earlier, one of the main workflows in R will be the following:
- Edit R functions
load_all()
(or keyboard shortcut Ctrl/Cmd+Shift+L)- Explore how R functions work, do they behave as you expect
- Repeat 1-3 until satisfied
- Perform
devtools::check()
Step 3 is this informal approach to clarifying that your code behaves as you expect. You could imagine as your code becomes more complex, you can’t always informally check everything in the console. This is where tests come in.
Compared to the informal approach of checking if the code behaves as you expect by working in the console, tests are a formal way to ensure that your code works as you would expect.
We are going to briefly touch on writing some very simple tests, but overall I think tests give you three benefits:
- They force you to revisit your own code. In doing so, you review your code to ensure it behaves as you expect, which also presents an opportunity to fix any bugs, or improve how you wrote the original code
- They give you freedom to make changes in your code. If you have a well-tested code R package, you are more or less free to make large scale changes on your code. If your tests fail, then you know you’ve broken something
- Outside confidence from others. If they see that your code has good tests, then it lends your code to having a high degree of trust.
There are a variety of testing approaches in R, we are going to use the testthat
package, as it integrates well with usethis
and devtools
.
You establish your tests with:
::use_testthat() usethis
This gives you the following output:
✔ Adding testthat to Suggests field in DESCRIPTION.
✔ Adding "3" to Config/testthat/edition.
✔ Creating tests/testthat/.
✔ Writing tests/testthat.R.
☐ Call usethis::use_test() to initialize a basic test file and open
it for editing.
This sets up some boilerplate code so you don’t have to worry about creating directories like tests/testthat
and make the other adjustments.
Let’s follow its advice and add a test with:
use_test("praise")
This gives us the following output:
✔ Writing tests/testthat/test-praise.R.
☐ Modify tests/testthat/test-praise.R.
It also opens a file named test-praise.R
, which saves you the hassle of creating that file.
It even comes with some example code!
test_that("multiplication works", {
expect_equal(2 * 2, 4)
})
This test reads as:
We expect 2*2 to be equal to 4
If you run the test, by running devtools::test()
in the console, you will see the test output.
Let’s write a test for our praise functions.
This will check that the praise contains the name of the person. We can do this with expect_message()
, a function taking two arguments: the object, and the expected contents. In our case:
test_that("Praise contains the name of the person", {
expect_message(praise("Nick"), "Nick")
})
This is essentially asking:
Does this praise message contain “Nick” ?
You can now run devtools::test()
in the console:
ℹ Testing praiseme
✔ | F W S OK | Context
✔ | 1 | praise
══ Results ══════════════════════════════════════════════════════════
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ]
- Add a unit test with:
test_that("Praise contains the name of the person", {
expect_message(praise("Nick"), "Nick")
})
Run
devtools::test()
Does it pass?Add another unit test - does it pass? Why? Why not?
test_that("Praise contains the name of the person", {
expect_message(praise("Nick"), "Chitra")
})
- (Extension). Explore snapshot testing - see the vignette, snapshotting, for more information.
From here, we are going to get our package up online on git and github!