Skip to content

Support for Testing callModule #4180

@alexverse

Description

@alexverse

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions