How to use testServer() with an arg that is a reactive?

shiny:testServer() looks amazing and I'm really looking forward to using it in my apps, so thank you to everyone who developed it.

I don't yet understand, though, how to provide an input arg that is itself a reactive. My use case looks something like this (although the real app is a bit more complicated):

First, a module for triggering some actions in other modules. This needs to be its own module, not a run button embedded in one module, since it interacts with several modules. It is also completely separate, visually, from the parts of the page used by the other modules.

## Module for triggering run/reveal menu action in several other modules
mod_run_ui <- function(id){
  ns <- NS(id)
  tagList(
    actionButton(inputId = ns("run"), 
                 label = "MAKE IT HAPPEN!"),
    HTML("</br></br>")
  )
}

mod_run_server <- function(input, output, session) {
  run_update <- reactive({
    return(input$run)
  })
  
  return(list(run_update = run_update))
}

I also have a module that uses requires input from other modules (here I'm simplifying it to requiring only one, from the run module above, but in reality it uses more).

## Module for running a model after the "run" module says it's time
mod_calc_ui <- function(id){
  ns <- NS(id)
  tagList(
    ## Some graphs and downloadable reports based on the model running
  )
}

mod_calc_server <- function(input, output, run_status) {
  ## Some model calculations
}

The app server then looks like this:

run_model <- callModule(mod_run_server, id = "run_models")

model1_results <- callModule(mod_calc_server, id = "model1_results", run_status = run_model$run_update)

So, in order to test mod_calc_server(), I need to provide a reactive as the argument for run_status. I understand that I can pass arguments for a module to testServer using the args parameter, but I'm not sure how to do this when the argument needs to be a reactive itself. Here is the example from this documentation, but since testServer() is called outside of a reactive context (I think), I don't know how to provide it with a reactive argument.

testServer(myModule2, args = list(multiplier = 3), {
  session$setInputs(x = 1)
  expect_equal(myreactive(), 3)
})

Hi,

it is possible to create a reactive outside of a reactive context, but you can only evaluate it inside a reactive context.

> reactiveVal(1)
reactiveVal: [1] "1" 
> reactiveVal(1)()
Error: Operation not allowed without an active reactive context.
* You tried to do something that can only be done from inside a reactive consumer.

Hence, you could create the reactive outside of the testServer call and then pass it on as an argument. Then inside of testServer you can access its values.

In your example, you are instantiating your module with a reactive as an argument. But for your testing you could equally well instantiate with a reactiveVal. Either work fine. So your call to testServer could look something like this:

reactiveVal: [1] "1" 
> reactiveVal(1)()
Error: Operation not allowed without an active reactive context.
* You tried to do something that can only be done from inside a reactive consumer.
Run `rlang::last_error()` to see where the error occurred.
> 

If you want to define runStatus as a reactive I guess you could write

runStatus <- reactive(reactiveVal(1))

but it seems unnecessary.

I should say that I have written the solution for an app whose module is defined as below.

mod_calc_server <- function(id, runStatus) {  
  moduleServer(id, function(input, output, session) {     
    ## Some model calculations
  })
}

and that is called in this way:

mod_calc_server("myid", run_status = run_model$run_update)

which is the recommended syntax. So if you experience any problems perhaps this difference could be a source of errors.

Good luck!

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.