Skip to content
AgileLogics
5 min read

Testing with testthat

Prerequisites: Error handling and defensive code

A test is code that checks your other code works. Tests catch mistakes when you change a function, and they document what the function is supposed to do. testthat is R's standard testing package.

Writing tests

library(testthat)

clean_price <- function(text) {
  value <- as.numeric(gsub("[^0-9.]", "", text))
  if (is.na(value)) stop("Could not parse a price from '", text, "'")
  value
}

test_that("clean_price strips currency symbols and commas", {
  expect_equal(clean_price("$1,299.50"), 1299.5)
  expect_equal(clean_price("€15"), 15)
})

test_that("clean_price rejects text without a number", {
  expect_error(clean_price("free"), "Could not parse")
})
Test passed with 2 successes 😀.
Test passed with 1 success 🥇.

(testthat adds a randomly chosen celebratory emoji, so yours may differ.)

  • test_that("description", { ... }) groups related expectations.
  • Expectations check results: expect_equal(), expect_true(), expect_identical(), expect_error(), expect_warning(), expect_length(), expect_s3_class().
  • expect_equal allows tiny floating-point differences, so decimals compare sensibly.

A failing test

When something breaks, testthat tells you exactly what differed:

── Failure: clean_price strips currency symbols and commas ──
clean_price("$1,299.50") (`actual`) not equal to 1299 (`expected`).

  `actual`: 1300
`expected`: 1299

Organizing tests

In a package (Level 3) or a larger project, tests live in tests/testthat/test-*.R files, and testthat::test_dir("tests/testthat") or devtools::test() runs them all.

What to test

Test the functions that clean and transform data — they're where subtle bugs hide. Include edge cases: empty input, missing values, unexpected text, and the boundaries of ranges.

Common mistakes

  • Testing with the same numbers you used while writing the function. Add awkward cases you didn't think of at first.
  • Comparing decimals with expect_identical, which fails on tiny rounding differences — use expect_equal.