Reputation: 99
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
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
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:
Upvotes: 5