[This article was first published on Rtask, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)


Want to share your content on R-bloggers? click here if you have a blog, or here if you don’t.

You can read the original post in its original format on Rtask website by ThinkR here: Expectations, Verified! Dive into the World of Unit Tests with expect_*()

Unit tests are essential in the development of an R package. They ensure that your functions work as expected while protecting you from regressions when you improve or modify your code.

Thanks to the {testthat} package, writing and automating tests in R becomes simple and intuitive. At the core of this approach are the expect_*() functions, which play a central role in validating the behaviors you expect from your code.

No time to read: What is it about?

In this article, we will explore the most used expect_*() functions, why they are indispensable, and how to implement them to write robust and maintainable tests.

First, let’s load the package:

library(testthat)

Why use expect_*()?

The expect_*() functions allow you to compare the actual behavior of your code with the expected results. Each test checks a specific “expectation” (hence the name “expect”), whether it’s a return value, an error, a warning, or even a user message. If a function does not meet this expectation, a test failure is reported, alerting you to a potential issue.

Let’s now look at the most commonly used expect_*() functions to test different aspects of your code.

expect_equal()

  • Use:

Checks that two objects are equal in value, allowing for minor differences (such as numerical imprecision).

  • Example:
test_that("Multiplication works", {
  result <- 2 * 3
  expect_equal(object = result, expected = 6)
})
#> Test passed 🎊

expect_identical()

  • Use:

Checks that two objects are strictly identical, including their attributes and structure.

  • Example:
test_that("Objects are strictly identical", {
  list1 <- list(a = 1, b = 2)
  list2 <- list(a = 1, b = 2)
  expect_identical(object = list1, expected = list2)
})
#> Test passed 🎉

Unlike expect_equal(), here the test fails if the objects differ in any attribute or detail, such as numerical precision:

test_that("expect_identical vs expect_equal with numerical tolerance", {
  num1 <- 0.1 + 0.2
  num2 <- 0.3
  # Test with expect_equal: This test will pass due to a small numerical tolerance
  expect_equal(object = num1, expected = num2)
  # Test with expect_identical: This test will fail because the comparison is strictly exact
  expect_identical(object = num1, expected = num2)
})

expect_true() et expect_false()

  • Use:

Checks that a logical expression is true or false.

  • Example:
test_that("True and false logic test", {
  expect_true(object = (1 + 1 == 2))
  expect_false(object = (1 + 1 == 3))
})
#> Test passed 🥇

expect_error()

  • Use:

Checks that a function triggers an error. You can also specify the expected error message for finer validation.

  • Example:
test_that("Error when input is negative", {
  expect_error(object = log("fake"), regexp = "non-numeric argument to mathematical function")
})

Here, the test only passes if running log("fake") generates an error with the message “non-numeric argument to mathematical function”.

⚠ : Only take the error message without the “Error in … :” part.

expect_message()

  • Use:

Checks that a function generates a message via the message() function, thus validating that your code properly informs the user.

  • Example:
test_that("Message displayed correctly", {
  expect_message(object = message("Hello !"), regexp = "Hello !")
})
#> Test passed 😸

expect_length()

  • Use:

Checks that the length of an object (vectors, lists, or other objects) is equal to the expected value.

  • Example:
test_that("Vector has correct length", {
  vec <- c(1, 2, 3)
  expect_length(object = vec, n = 3)
})
#> Test passed 😀

expect_type()

  • Use:

Checks that the tested object is of the expected type (such as “double”, “character”, “data.frame”, etc.). This is useful for validating that functions return objects of the appropriate type.

  • Example:
test_that("Object type is correct", {
  num <- 42
  expect_type(object = num, type = "double")
  vec <- c(TRUE, FALSE, TRUE)
  expect_type(object = vec, type = "logical")
})
#> Test passed 😀

Conclusion: Test Your Expectations Precisely

The expect_*() functions from the {testthat} package are powerful tools for writing unit tests in R. They allow you to cover a wide range of use cases, whether it’s verifying numerical values, errors, warnings, or object types. By integrating these tests into your workflow, you increase the robustness of your code and simplify its long-term maintenance.

So, what are you waiting for? Dive into the world of unit tests, write your first expectations with expect_*(), and ensure that your R functions always meet the defined expectations!

To go further

For those who want to go further and master the art of creating robust packages, including best practices for unit testing, check out our training

This post is better presented on its original ThinkR website here: Expectations, Verified! Dive into the World of Unit Tests with expect_*()

To leave a comment for the author, please follow the link and comment on their blog: Rtask.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you’re looking to post or find an R/data-science job.


Want to share your content on R-bloggers? click here if you have a blog, or here if you don’t.

Continue reading: Expectations, Verified! Dive into the World of Unit Tests with expect_*()

Long-term Implications and Future Developments of Unit Tests in R

Unit tests have become an essential aspect of the development of R packages. The concept is significant to ensure that functions perform as expected and protects against regressions while improving or changing the code. The rise of packages like {testthat} have simplified the process of writing and automating tests in R, with expect_*() functions at the core of validating code behavior.

The Future of Unit Tests in R

The importance of unit tests is expected to grow in the future as more complex and intricate codes are developed. By using the expect_*() functions packages like {testthat}, developers can easily discover bugs, simplify the debugging process, and increase the quality of their packages. As R continues to expand and be incorporated into more diverse projects, the value of thorough, well-written unit tests will perpetually rise.

Actionable Advice: Improving Code Quality and Maintenance with Unit Tests

The best advice based on this information is to integrate these unit tests into your R programming workflow. This will not only streamline the development process but also increase the robustness and maintainability of packages. Dive right into the world of unit tests, write your first expectations with expect_*(), and ensure that your R functions consistently meet the expectations.

The Key expect_*() Functions

  • expect_equal(): Ensures two objects are equal in value with minor differences.
  • expect_identical(): Checks strict equality, including structure and attributes of two objects.
  • expect_true() and expect_false(): Checks the truth or falsehood of a logical expression.
  • expect_error(): Verifies if a function triggers an error.
  • expect_message(): Checks if a function triggers a user message.
  • expect_length(): Tests for the specified length of an object.
  • expect_type(): Verifies the type of the tested object.

Conclusion

Unit tests, specifically the expect_*() functions of the {testthat} package, offer developers a powerful suite of tools for penning unit tests in R, ensuring the robustness and maintainability of the code. Utilizing and integrating these tests during the development process of each R package leads to better code quality and eases long-term maintenance. Adoption of such practices can be expected to grow in the future, making unit tests an instrumental part of R package development.

Read the original article