Feedback on Shiny features & changes

Greetings! We're working on some new things in Shiny and would like to get feedback from the community. Briefly, the two changes are: 1.) automatically loading the R/ directory, and 2.) a new integration testing framework to help with testing the interactions between reactives. We felt that these changes were necessary before we could start laying out a vision for some "best practices" around developing complex Shiny applications that are stable, robust, and maintainable.

Both changes are available on the master branch of Shiny on GitHub now, but are not yet released to CRAN. To preview the changes, you'll need to install the development version of Shiny using remotes::install_github("rstudio/shiny"). We're actively working on writing up better documentation to help Shiny users be successful with these changes, but I wanted to solicit some early feedback from the community here.

Automatically Loading the R/ directory

The first change is that we automatically source any .R files in the top level of the R/ directory adjacent to your application. i.e. if your directory looked like:

+- app.R
+- R/
| +- helper1.R
| +- another.R

both R/helper1.R and R/another.R would be automatically loaded and made available to the code in app.R. The hope here is to make it easier to pull your Shiny modules or utility functions into separate files without the burden of having to remember to source() those files yourself.

Additional details about this change (and how to opt-out) are available here.

New Integration Testing Framework

There are many different types of automated testing, but in our opinion a comprehensive testing strategy for a complex Shiny app might cover:

  1. unit testing to test the individual functions in your app (testthat or a similar package)
  2. integration testing to test the interactions between different functions in your app
  3. functional testing to test the overall function of the app from the user's perspective in a browser (shinytest, shinyloadtest)

Because Shiny relies so heavily on reactivity, there wasn't an obvious way to cover the second type: integration testing. This became especially painful when you wanted to test Shiny modules which make a lot of assumptions about having a reactive environment.

The recent changes introduce testModule and testServer functions for testing modules and applications, respectively. A simple application like this one

# app.R
ui <- fluidPage(
    textInput("name", "Name: "),
    textOutput("greeting")
)

server <- function(input, output) {
    output$greeting <- renderText({
        paste0("Hello, ", input$name, "!")
    })
}

shinyApp(ui = ui, server = server)

Could be tested with code like

library(testthat)
testServer({
  session$setInputs(name="Shiny User")
  expect_equal(output$greeting, "Hello, Shiny User!")
  
  session$setInputs(name="New Name")
  expect_equal(output$greeting, "Hello, New Name!")
})

We've written up some more comprehensive documentation which demonstrate additional features of the framework including simulating the passing of time for invalidateLater and friends or testing individual modules in isolation.

Conclusion

If you have any feedback on either of these changes, please comment on this post. We'd love to hear any feedback you have (positive, negative, or neutral!). Do you envision yourself leveraging either of these changes in your Shiny apps?

7 Likes