
Testing

Figure adapted from Packages, by Hannah Frick
BLT βββ BLT.Rproj βββ DESCRIPTION βββ NAMESPACE βββ R β βββ AR1.R β βββ BLT-package.R β βββ data.R β βββ perc_missing.R β βββ perc_missing_tidy.R βββ README.Rmd βββ README.md βββ data β βββ bacon.rda β βββ lettuce.rda β βββ tomatoes.rda βββ man β βββ AR1.Rd β βββ BLT-package.Rd β βββ bacon.Rd β βββ figures β β βββ README-pressure-1.png β βββ perc_missing.Rd β βββ perc_missing_tidy.Rd βββ vignettes βββ BLT.Rmd βββ BLT.html
unchanged
changed
changed by you

Figure adapted from Packages, by Hannah Frick
BLT βββ BLT.Rproj βββ DESCRIPTION βββ NAMESPACE βββ R β βββ AR1.R β βββ BLT-package.R β βββ data.R β βββ perc_missing.R β βββ perc_missing_tidy.R βββ README.Rmd βββ README.md βββ data β βββ bacon.rda β βββ lettuce.rda β βββ tomatoes.rda βββ man β βββ AR1.Rd β βββ BLT-package.Rd β βββ bacon.Rd β βββ figures β β βββ README-pressure-1.png βββ tests β βββ testthat β β βββ test-perc_missing_tidy.R β βββ testthat.R βββ vignettes βββ BLT.Rmd βββ BLT.html
unchanged
changed
changed by you
When we run:
The objects tomatoes_miss and bacon_miss should be:
So we mightβ¦
Try these:
β¦is informal testing. We:
perc_missing_tidy()devtools::load_all()perc_missing_tidy() interactivelyperc_missing_tidy() if neededdevtools::load_all()perc_missing_tidy() interactivelyProblem: you forget all the interactive testing youβve done
Solution: have a system to store and re-run the tests!
Fewer bugs: you are explicit about behaviour of functions.
Encourages good code design. If it is hard to write unit tests your function may need refactoring
Opportunity for test-driven development
Robustness
Read more about testing in the chapter in R Packages.
.
βββ DESCRIPTION
βββ LICENSE
βββ LICENSE.md
βββ man
β βββ AR1.Rd
β βββ bacon.Rd
β βββ figures
β β βββ README-pressure-1.png
β βββ perc_missing.Rd
β βββ perc_missing_tidy.Rd
βββ NAMESPACE
βββ R
β βββ AR1.R
β βββ BLT-package.R
β βββ data.R
β βββ perc_missing.R
β βββ perc_missing_tidy.R
βββ README.md
βββ tests
β βββ testthat
β β βββ test-perc_missing_tidy.R
β βββ testthat.R
βββ BLT.Rprojtests/testthat/test-xxxx.Rtests/testthat.R: runs the tests when devtools::check() is calledtest-xxxx.R contains several tests. Might be:
expect_zzzz(actual_result, expectation)actual_result == expectation no erroractual_result != expectation Errortestthat: usethis::use_testthat(3) ONCEusethis::use_test()testthat::test_file()devtools::test() and devtools::check()To set up your package to use testthat: usethis::use_testthat(3) which:
tests/testthat/: this is where the test files liveedits DESCRIPTION:
Suggests: testthat (>= 3.0.0)Config/testthat/edition: 3tests/testthat.R: this runs the test when you do devtools:check() DO NOT EDIT㪠Set up your package to use testthat:
3 means testthat edition 3 (testthat 3e)
As well as installing that version of the package, you have to explicitly opt in to the edition behaviours.
Before we try to make a test, letβs look at some of the expect_zzzz() functions we have available to us.
Form: expect_zzzz(actual_result, expectation)
expectation is what you expectactual_result is what you are comparing to the expectationexpect_zzzz() have additional argumentsSome types of expect_zzzz()
expect_identical()expect_equal()expect_true()expect_named()expect_error()expect_warning()We can set the wiggle room:
# vector of named values
x <- c(a = 1, b = 2, c = 3)
# test whether x has names: no error
expect_named(x)
# test if the names are "a", "b", "c": no error
expect_named(x, c("a", "b", "c"))
# test if the names are "b", "a", "c": error
expect_named(x, c("b", "a", "c"))Error:
! Expected `x` to have names `c("b", "a", "c")`.
Differences:
`actual`: "a" "b" "c"
`expected`: "b" "a" "c"
For next time!
Letβs look at the tests in imputeTS.
We can also check the coverage of the tests.
You can create and open (or just open) a test file for blah.R with use_test().
π¬ Create a test file for perc_missing_tidy.R
Open perc_missing_tidy.R in your editor
For example:
Generally:
You list the expectations inside test_that( , { })
You can have as many expectations as you need.
Ideally, 2 - 6 ish or consider breaking the function into simpler functions.
We will add two expectations:
perc_missing_tidy() is a tibble with expect_true()perc_missing_tidy() has columns with the right names with expect_named()We will do them one at a time so you can practice the workflow.
test-perc_missing_tidy.R㪠Add a test to check the output of perc_missing_tidy() is a tibble with expect_true():
We use perc_missing_tidy() and examine the output with an expectation.
π¬ Run the test with testthat::test_file()
ββ Testing test-perc_missing_tidy.R ββββββββββββββββββββββ
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ] Done!π₯³
You can also use the βRuns Testsβ button
devtools::test()π¬ Run all the tests with devtools::test()
βΉ Loading BLT
βΉ Testing BLT
β | F W S OK | Context
β | 1 | matches [0.4s]
ββ Results ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Duration: 0.5 s
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ]
π Your tests are the bee's knees πNow you will test the output of perc_missing_tidy() to make sure it has columns with the right names expect_named()
we expect the name(s) to be:
βpercβ
π¬ Use expect_named() in test-perc_missing_tidy.R to check the column names of tomatoes_miss
test_that("perc_missing_tidy creates tibble", {
# use the function
tomatoes_miss <- tomatoes |>
perc_missing_tidy(value)
expect_true(tibble::is_tibble(tomatoes_miss))
})
test_that("perc_missing_tidy has correct names", {
# use the function
tomatoes_miss <- tomatoes |>
perc_missing_tidy(value)
expect_named(
tomatoes_miss,
c("perc")
)
})These can also be combined into one test so you donβt need to repeat yourself!
BLT βββ BLT.Rproj βββ DESCRIPTION βββ NAMESPACE βββ R β βββ AR1.R β βββ BLT-package.R β βββ data.R β βββ perc_missing.R β βββ perc_missing_tidy.R βββ README.Rmd βββ README.md βββ data β βββ bacon.rda β βββ lettuce.rda β βββ tomatoes.rda βββ man β βββ AR1.Rd β βββ BLT-package.Rd β βββ bacon.Rd β βββ figures β β βββ README-pressure-1.png βββ tests β βββ testthat β β βββ test-perc_missing_tidy.R β βββ testthat.R βββ vignettes βββ BLT.Rmd βββ BLT.html
unchanged
changed
changed by you
π¬ Run the edited test file
Or the βRuns Testsβ button
βΉ Loading BLT
βΉ Testing BLT
β | F W S OK | Context
β | 2 | matches [0.2s]
ββ Results ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Duration: 0.2 s
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 2 ] π₯³
Test coverage is the percentage of package code run when the test suite is run.
covr packageThere are two functions you might use interactively:
coverage on the active file: devtools::test_coverage_active_file()
coverage on the whole package:devtools::test_coverage()
π¬ Make sure perc_missing_tidy.R is active in the editor and do:

π¬ Check the coverage over the whole package:

Of course thereβs a usethis function!
Now would be a good time to commit your changes.
Jason Long, CC BY 3.0 <https://creativecommons.org/licenses/by/3.0>, via Wikimedia Commons

Figure adapted from Packages, by Hannah Frick
BLT βββ BLT.Rproj βββ DESCRIPTION βββ NAMESPACE βββ R β βββ AR1.R β βββ BLT-package.R β βββ data.R β βββ perc_missing.R β βββ perc_missing_tidy.R βββ README.Rmd βββ README.md βββ data β βββ bacon.rda β βββ lettuce.rda β βββ tomatoes.rda βββ man β βββ AR1.Rd β βββ BLT-package.Rd β βββ bacon.Rd β βββ figures β β βββ README-pressure-1.png βββ tests β βββ testthat β β βββ test-perc_missing_tidy.R β βββ testthat.R βββ vignettes βββ BLT.Rmd βββ BLT.html
unchanged
changed
changed by you
testthat βtries to make testing as fun as possibleβtests/testthat/test-xxxx.Rtest_that("something works", { *expectations* })tests/testthat.R: runs the tests and should not (normally) be editedexpect_zzzz(actual_result, expectation)usethis::use_testthat(3) sets up your package to use testthatusethis::use_test(xxxx) creates test-xxxx.Rtestthat::test_file() runs the tests in a test filedevtools::test() runs the tests in all the test filesdevtools::test_coverage_active_file()devtools::test_coverage()testthat documentation