David
David

Reputation: 99

Testing modular R Shiny (golem) dashboards

I've been exploring (**and loving) the golem package for developing modular dashboards with R Shiny. But I'm struggling to wrap my head around how to test a modular dashboard.

For example in the repex below how would I test that if the input$n_rows in the import module is set to 15 that the output in the display module contains 15 rows?

I'd be incredibly grateful for any support on this!


library(shiny)
library(reactable)
library(dplyr)

# Import module UI
mod_import_ui <- function(id){
  
  ns <- NS(id)
  
  fluidRow(
    # Allow the user to select the number of rows to view
    numericInput(ns("n_rows"), 
                 "Select number of observations",
                 value = 10)
    
  )
}

# Import module Server
mod_import_server <- function(id){
  
  moduleServer(
    id,
    function(input, output, session){
      
      data <- reactive({
        
        # Sample the requested number of rows from mtcars and return this to the application server
        mtcars %>%
          slice_sample(n = input$n_rows)
        # [....] # Some complex formatting and transformations
        
      })
      
      return(data)
      
      
      
    }
  )}

# Display module UI
mod_display_ui <- function(id){
  
  ns <- NS(id)
  
  fluidRow(
    
    reactableOutput(ns("table"))
    
  )
}

# Display module Server
mod_display_server <- function(id, data_in){
  
  moduleServer(
    id,
    function(input, output, session){
      
      # [....] # Some more transformations and merging with data from other modules
      
      output$table <- renderReactable(reactable(data_in()))
      
    }
  )}


app_ui <- function(request) { 
  
  tagList(
  
    mod_import_ui("import_1"),
    mod_display_ui("display_1")

  )
  
  }


app_server <- function(input, output, session) { 
  
  data_in <- mod_import_server("import_1")
  mod_display_server("display_1", data_in)
  
}

shinyApp(ui = app_ui, server = app_server)


Upvotes: 3

Views: 1189

Answers (2)

Colin FAY
Colin FAY

Reputation: 5109

As a complement to Sebastien's answer, I would like to point that starting with {shiny} v 1.5.0, you can test server functions directly using the testServer function, which might be what you are looking for.

Here is a reprex of how you can achieve that:

library(shiny)
library(magrittr)
library(dplyr)

mod_import_server <- function(id){
  moduleServer( id, function(input, output, session){
    data <- reactive({
      mtcars %>%
        slice(n = 1:input$n_rows)
    })
    return(data)
  })
}

shiny::testServer(mod_import_server, {
  
  for (i in 1:10){
    
    session$setInputs(n_rows = i)
    testthat::expect_equal(
      data(), 
      slice(mtcars, n = 1:i)
    )
    
  }
})

Here, you can test that your reactive() behave the way you're expecting. That's not perfect but a good start :) It's hard to find a way to test for the behaviour of the second module though, as it depends on a reactive() passed as a value.

Colin

Upvotes: 4

S&#233;bastien Rochette
S&#233;bastien Rochette

Reputation: 6671

I would recommend separating the core of the app from the user interface.

The {golem} framework allows building your application inside a R package, which means you can use all tools from package build to document and test your code.
If you follow our guide in engineering-shiny.org/, you will see that we recommend extracting all R code from your "server" part to test it in a vignette, transform it as a regular function, so that you can test it as usual with R packages.
Hence, you ShinyApp only calls internal functions, already documented and tested. With this approach, you can test outputs of different scenarios that can happen in your application. Try different input parameters in a static script and verify outputs, whatever you change in your app in the next steps of your development.

The book gives a lot of advices. If I had to sum them up for a workflow, this would be:

  1. Build the necessary code in an Rmd directly. This allows you to test the operation without having to go through all the necessary clicks. We call it the "Rmd first" method: https://rtask.thinkr.fr/when-development-starts-with-documentation/
  2. Factorize this code into R functions to put as little as possible in the Shiny app itself.
  3. Create your UI part without server (or not too much), just to see what the general appearance looks like
  4. Include your functions in the appropriate places in the app.
  5. Strengthen the code. Reproducible examples, unit tests, documentation, code versioning, ... (This step is better when done in parallel with the code)

Upvotes: 5

Related Questions