-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Context
The current Shiny documentation emphasizes that moduleServer is the recommended approach and can be tested using testServer, whereas callModule does not have direct testing support. However, many legacy Shiny applications still rely on callModule, making it difficult to refactor or maintain without the ability to write tests.
Proposed Solution
We can implement a function, testCallModule(), that enables testing for callModule by evaluating the module's server-side expression as a server function with arguments bound in its environment. This allows us to inject the testing expression into testServer.
Here's the code:
testCallModule <- function(
module_server,
expr,
args = list(),
session = MockShinySession$new()
) {
required_args <- c("input", "output", "session")
rlang::check_required(module_server, required_args)
server <- function(input, output, session) {
module_args <- rlang::fn_fmls(module_server) |>
purrr::discard_at(required_args) |>
purrr::list_assign(!!!args)
module_body <- rlang::fn_body(module_server)
rlang::env_bind(rlang::current_env(), !!!module_args)
rlang::eval_bare(module_body)
}
shiny::testServer(
app = server,
expr = !!rlang::enexpr(expr),
session = session
)
}Usage Example
Here’s how this function could be used in practice:
myModule <- function(input, output, session, prefix = "") {
output$result <- renderText({
paste0(prefix, toupper(input$txt))
})
}
testCallModule(myModule, args = list(prefix = "foo"), {
session$setInputs(txt = "bar")
result <- output$result
expect_equal(result, "fooBAR")
})Request
Could this functionality be officially supported in Shiny? It offers a way to refactor and stabilize legacy code.
If the Shiny team sees value in this, I’d happily open a PR to contribute to this functionality.