Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better documentation for mocking base variables with local_mocked_bindings #2033

Open
chriscarrollsmith opened this issue Nov 26, 2024 · 1 comment

Comments

@chriscarrollsmith
Copy link

chriscarrollsmith commented Nov 26, 2024

The documentation indicates that to mock base objects, you should bind them to null somewhere in your package:

Base functions
To mock a function in the base package, you need to make sure that you have a binding for this function in your package. It's easiest to do this by binding the value to NULL. For example, if you wanted to mock interactive() in your package, you'd need to include this code somewhere in your package:

interactive <- NULL

Why is this necessary? with_mocked_bindings() and local_mocked_bindings() work by temporarily modifying the bindings within your package's namespace. When these tests are running inside of R CMD check the namespace is locked which means it's not possible to create new bindings so you need to make sure that the binding exists already.

But I am at a loss as to how this is supposed to work. I am trying to test the following function:

#' Determine operating system
#' @return Character string: "windows", "macos", or "linux"
get_os <- function() {
  if (get_platform()$OS.type == "windows") {
    return("windows")
  } else if (get_sysinfo()["sysname"] == "Darwin") {
    return("macos")
  } else {
    return("linux")
  }
}

I tried binding the following variables to NULL in my package's globals.R file:

.Platform <- NULL
Sys.info <- NULL

This successfully got the mock working, but it broke the actual package functionality:

get_os()
Error in if (.Platform$OS.type == "windows") { :
  argument is of length zero

Which makes sense to me, because by binding to NULL, we are overwriting the R variables our function needs in order to work. What is the logic here, and how do I do this without breaking my package? I also tried this, to no avail:

globalVariables(c(
  ".Platform",
  "Sys.info"
))
@chriscarrollsmith
Copy link
Author

chriscarrollsmith commented Nov 26, 2024

Here's what worked for me, and what I think the documentation should recommend instead: creating aliases for the base R objects.

get_platform <- function() .Platform
get_sysinfo <- function() Sys.info()

#' Determine operating system
#' @return Character string: "windows", "macos", or "linux"
get_os <- function() {
  if (get_platform()$OS.type == "windows") {
    return("windows")
  } else if (get_sysinfo()["sysname"] == "Darwin") {
    return("macos")
  } else {
    return("linux")
  }
}
test_that("get_os correctly identifies operating system", {
  # Test Windows detection
  local_mocked_bindings(
    get_platform = function() list(OS.type = "windows")
  )
  expect_equal(get_os(), "windows")
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant