I am calling a function from base in my code and I want to mock this function in my testthat
unit test.
How can I do this?
library(testthat)
my.func <- function() {
return(Sys.info()["sysname"]) # e. g. "Linux"
}
my.func()
# sysname
# "Linux"
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")), # see edit 2 !!!
expect_equal(my.func(), "Clever OS", fixed = TRUE)
)
)
# Error: Test failed: 'base function can be mocked'
# * my.func() not equal to "Clever OS".
?with_mock
says:
Functions in base packages cannot be mocked, but this can be worked around easily by defining a wrapper function.
I could encapsulate the base function call to Sys.info()
by a wrapper function that I call from my.func
then but let's assume I cannot do this because I am testing a function from a package that I cannot change...
Any solution for this?
I am using R3.4.4 64 Bit on Ubuntu 14.04 with testthat 2.0.0.9000.
Edit 1:
Using
`base::Sys.info` = function() return(list(sysname = "Clever OS"))
results in the testthat
error msg:
Can't mock functions in base packages (base)
Edit 2: As @suren show in his answer my code example here is wrong (the mocked function returns another class then the original one :-(
The correct mock function should be: Sys.info = function() return(c(sysname = "Clever OS"))
It's possible to use Google Mock to mock a free function (i.e. a C-style function or a static method). You just need to rewrite your code to use an interface (abstract class). public: ... virtual bool Open(const char* path, const char* mode) { return OpenFile(path, mode); } };
Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new , and allowing test-time configuration of return values.
CallBase , when initialized during a mock construction, is used to specify whether the base class virtual implementation will be invoked for mocked dependencies if no setup is matched. The default value is false . This is useful when mocking HTML/web controls of the System.
The error message is my.func() not equal to "Clever OS"..
The reason is that Sys.info
returns a named character vector while your mocking function a list
.
Just modify the mocking function and the expected value and it works:
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(c(sysname = "Clever OS")),
expect_equal(my.func(), c(sysname = "Clever OS"), fixed = TRUE)
)
)
This works even within a package.
Note: Mocking of base functions shouldn't work according to the help of with_mock
but it does (at the moment at least).
The following (my.func()$`sysname`) seems to pass the test with the original code from the question.
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")),
expect_equal(my.func()$`sysname`, "Clever OS", fixed = TRUE)
)
)
Alternatively, have a list where the string in expect_equal
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")),
expect_equal(my.func(), list(`sysname` = "Clever OS"), fixed = TRUE)
)
)
Warning about mocking base functions using with_mock
:
Even though mocking of base functions may work in case of my question and the accepted answer of @Suren many issues around with_mock
in the package testthat
disencourage to mock base package functions (or even functions outside of the package under test), e. g.
testthat 2.0.0 - Breaking API changes
mockery
or mockr
instead.
Don't allow base packages to be mocked
with_mock() function seems to interact badly with the JIT compiler
Prevent with_mock from touching base R packages
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With