[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: Mock Them All: Simulate to Better Test with testthat

Unit testing in R. You know, those small functions that ensure your code works flawlessly—even when you’re on vacation or writing new modules at 2 a.m.
But let’s be honest: when it comes to testing user interactions or external resources, things can quickly turn into a headache.
What do you do when your code requires a file selection dialog or a connection to an API that takes forever to respond?
No way you want to block everything just for a test!

In those cases, it’s time to get clever. The idea isn’t to give up on testing, but to cleverly bypass what’s problematic. That’s where a well-known technique comes in: mocking.
Luckily, R and the testthat package provide tools just for this.

Mocking means temporarily replacing a function or resource with a fake, controlled version that simulates the expected behavior.

Intrigued? Let’s dive in.

The Art of Pretending: Simulating user interactions

Imagine a function that asks the user to select a file.
If you had to test that function with a real dialog box, it would be a total waste of time—not to mention impossible to automate easily in a CI environment.
That’s where the magic of mocking comes in.

Here’s an example of such a function:

select_file <- function() {
  file_path <- choose.files()  # Cette fonction ouvre une boîte de dialogue
  cat("You have chosen the file :", file_path)
}

You don’t want to manually select a file every time you run the test.
With with_mocked_bindings() from the {testthat} package, you can replace choose.files with a version that simply returns a predefined path.
This avoids any interaction and makes the test instant:

test_that("select_file works without a dialog box", {
  with_mocked_bindings(
    code = {
      expect_output(select_file(), "You have chosen the file : C:/fakedir/fakefile.txt")
    },
    choose.files = function() {"C:/fakedir/fakefile.txt"}
  )
})

And that’s it! You test the function as if the user already selected a file—no dialog box involved.

When simulation becomes essential: Testing external resources

Unit tests are often used to verify functions that rely on external resources such as APIs, databases, or network interactions.
Imagine a function that retrieves data from an API.
Actually calling the API during every test would be slow, costly, and completely unnecessary.
Instead, you can simulate the API response. Here’s how:

fetch_data <- function() {
  response <- httr::GET("https://api.exemple.com/data")
  content <- httr::content(response)
  return(content)
}

To test this function without making a real API call, you can mock the response using with_mocked_bindings.

Here’s an example where the API call is replaced with a handcrafted response:

Now you’re testing the function without ever hitting the real API, and you have full control over what’s returned.
You can simulate different situations (successful response, error, timeout, etc.) without depending on the actual API state.

⚠ Not everything can be mocked: The limitations

Some functions can’t be mocked directly, for example:

Sys.time()
Sys.getenv()

Trying to override them with with_mocked_bindings() results in:

Error in `local_mocked_bindings(...)`: Can't find binding for `Sys.time`

Why? Because certain functions like Sys.time() and Sys.getenv() are primitive functions in R.
They’re deeply embedded in the language core and not regular R objects you can easily override like user-defined functions.

🩹 The workaround: Encapsulate the behavior

The solution: create a wrapper function in your code that you can mock.

Example:

# Dans votre package
get_current_time <- function() { Sys.time()}
say_hello <- function() {
  if (format(get_current_time(), "%H") == "12") {
    "Time for lunch!"
  } else {
    "Just a little longer..."
  }
}

Et dans vos tests :

test_that("say_hello correctly detects noon", {
  with_mocked_bindings(
    code = expect_equal(say_hello(), "Time for lunch!"),
    get_current_time = function() as.POSIXct("2025-04-25 12:00:00")
  )
})

🚪 In summary

  • with_mocked_bindings() allows you to simulate functions whose behavior varies depending on user input, system state, or time, in an elegant way;
  • Some functions can’t be mocked directly (like Sys.time, Sys.getenv): wrap them in a separate function;
  • It’s a great way to create fast, reliable, non-interactive tests.

This post is better presented on its original ThinkR website here: Mock Them All: Simulate to Better Test with testthat

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: Mock Them All: Simulate to Better Test with testthat

Long Term Implications and Future Developments in R Testing

The blog post examined a vital technique used in R testing, called ‘mocking’ which implies creating false, manageable versions of your function or resource that deploy as expected. This approach can significantly assist in simplifying the testing of user interactions and other external resources which can often prove to be problematic.

Simulating User Interactions

In the future, developers might need to increasingly leverage the potential of ‘mocking’ for simulating user interactions. The method of ‘pretending’ would ease the testing of a function requiring user input significantly. Mocking can replace the real function with a version that returns a predefined path, removing the need for manual interaction, costing time and money.

Testing External Resources

The practice of mocking could revolutionize the testing of functions relying on external resources like APIs, databases, or any network interferences. Current real-time tests can prove to be slow, costly, and unproductive. In comparison, directly mocking the response from an API ensures an efficient, cost-friendly, and necessary solution.

Limitations and Workarounds

While mocking provides robust solutions, it has certain limitations. Primitive functions like Sys.time() and Sys.getenv() deeply integrated with the language core cannot be mocked. However, a future workaround could be to encapsulate the behavior by creating a wrapper function in your code that can be mocked. This would certainly be a reliable path to consider in the long term for functions that can’t be mocked directly.

Actionable Advice

  • Use Simulation for User Interactions: Mocking can assist in replacing the need for manual selection, saving both time and costs. Consider using with_mocked_bindings() from the {testthat} package to replace functions that require user interactions, thus causing tests to be run instantaneously.
  • Simulate Responses for External Resources: Functions relying on external resources can be tested without making a real-time call. Use simulation for the response from APIs and databases to efficiently test your functions.
  • Overcome Limitations: In cases of testing primitive functions that cannot be mocked directly, consider encapsulating the behavior by creating a mockable wrapper function in your code. This approach can resolve the issues faced when primitive functions cannot be overridden.

Overall, the long-term approach to R testing could be significantly transformed with the increased use of simulation or ‘mocking’. By understanding and leveraging this strategy, expectations can be set beforehand, and testing can be conducted effectively and efficiently.

Read the original article