How to use box
modules and mock dependencies when unit testing using testthat
?
#379
Replies: 2 comments 1 reply
-
I've been having a long think about how I can get what I want to work and just to better understand the issue at hand. I think what I want is I want to be ablet to manipulate the search path for box. This is my setup:
Where: index.R box::use(./R/hello_world[ hello_world ])
hello_world() test.R # somehow set the path so it will get the print_message function from the stub folder instead
options(box.path = c("stubs", "R"))
box::use(./R/hello_world[ hello_world ])
hello_world() R/hello_world.R box::use(print_message[ print_message ])
#' @export
hello_world <- function() {
print_message()
} R/print_message.R #' @export
print_message <- function() {
message("Hello, world!")
} stubs/print_message.R #' @export
print_message <- function() {
message("Goodbye, world!")
} When I run this I get: (base) work@Derecks-MacBook-Air research % Rscript test.R
Loading .Rprofile.
Setting conda environment to base.
Multi-threading for data.table using: 7 cores.
Error in box::use(./R/hello_world[hello_world]) :
there is no package called ‘print_message’
Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow
Execution halted So what I want is:
I understood that if I use The reason I want this is because I want to be able to "mock" my own functions. I am writing unit tests for a package that interacts with an API and I am having a lot of trouble with this. https://github.com/dereckmezquita/kucoin/tree/TRADE-2-kucoin2 Any help would be greatly appreciated! |
Beta Was this translation helpful? Give feedback.
-
I believe that the mocking support in ‘testthat’ only supports packages, no other scenario whatsoever. That’s a shame but it’s a built-in hard requirement that we can’t easily circumvent. In particular, the However, even if ‘testthat’ wasn’t hard-wired to expect packages, it would presumably only support mocking “bare” names, not fully-qualified module names, nor dynamically loaded names. That is, a generic mocking library could (at least conceptually) easily support mocking box::use(./nested[bar])
#' @export
foo = function () {
bar()
} But there’s basically no reasonable chance to support the following scenarios without having dedicated ‘box’ mocking support: box::use(./nested)
#' @export
foo1 = function () {
nested$bar()
}
#' @export
foo2 = function () {
box::use(./nested[bar])
bar()
} And the reasons are different for
I definitely see the need for this functionality. Unfortunately I am currently strapped for time and I can’t work on ‘box’ very much (that’s also why I have removed the sponsorship button). And adding this functionality would not be trivial (and probably doesn’t belong into ‘box’ itself but rather a dedicated testing/mocking package). Before ‘testthat’ shipped with built-in mocking support, I built my own mock helper which I use in a number of package tests. It does not support ‘box’ modules either but I think its general approach could be extended to support modules. Currently, it supports overriding functions that are either findable in the current environment (including in parent environments), or by specifying an explicit package name (it does not currently support overriding lazy data though, since that uses a completely different mechanism): my_sum = function (...) sum(...)
with_mock(
sum := \(a, b) a * b,
utils::checkHT := \(...) stop('intercepted!'), # called inside `utils:::head.function`
{
message(my_sum(2, 3)) # prints 6
head(my_sum) # Error: intercepted!
}
) This syntax could be extended to support mocking functions from modules as follows: with_mock(
./mod/a$bar := \() 'mocked bar',
{
box::use(alias = ./mod/a)
message(alias$foo())
}
) The idea here is that the mock setup needs to specify the target module in the same way as For completeness, here’s my implementation of the Implementation of
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello,
I’m encountering difficulties when attempting to unit test functions defined in a box module. My module defines a function that, in turn, imports several helper functions (e.g. build_headers, process_kucoin_response, etc.). I then import that function into my test file using box. However, when I try to mock these internal dependencies using testthat’s mocking facilities (such as
local_mocked_bindings()
), I run into errors.For example, one of the errors I receive is:
This error occurs when I attempt something like:
It appears that the functions imported into my module via box are not available as modifiable bindings in the module’s environment. I suspect that it is because box locks the environment or restructures the namespace such that these imported functions cannot be overridden by testthat’s mock tools.
I suspect the issue stems from how box handles module environments and imports, possibly locking them or not exposing imported functions for modification.
Has anyone successfully implemented unit tests with mocking in a box based project using testthat? Are there any recommended strategies or workarounds to allow for dependency mocking in box modules?
Beta Was this translation helpful? Give feedback.
All reactions