Shiny Tutorial: Input Validation + Shiny Testing Part 1

Read time: 5 minutes

Hi! Welcome to part 1 of this brief tutorial on shiny input validation and shiny app testing :)

This tutorial was inspired by my previous job where I am led the redevelopment of a Shiny application to conduct sensitivity analyses for causal inference methods. The team is almost finished with development efforts and you can access the beta version here if you’re curious :)

Ok, let’s jump into it!

Basic Example

To get started, I’ve created a simple example to help with the understanding of when you might want to use input validation and shiny test. To illustrate these uses, I’ve created a basic combinations calculator (with replacement). For example, let’s say we want to calculate the number of possible combinations to unlock my phone given a 6 digit password with 10 possible digits (digits 0 - 9) to which the any of these possible digits can show up more than one time (i.e. combination with replacement) in my 6 digit password (e.g., the number 2 can show up more than one time in my 6-digit password). I’ve provided the code below of the simple combinations calculator app:

library(shiny)

# Define UI for application
ui <- fluidPage(

    # Application title
    titlePanel("Combination Calculator Without Replacement"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            numericInput("objects",
                        "Number of objects (n)",
                        min = 1,
                        max = 50,
                        step = 1,
                        value = 0),
            numericInput("sample",
                         "Number in sample (r)",
                         min = 1,
                         max = 50,
                         step = 1,
                         value = 0),
            actionButton("go", "Calculate")
        ),

        # Show a plot of the generated distribution
        mainPanel(
          textOutput("combinations")
        )
    )
)

# Define server logic to calculate combinations
server <- function(input, output) {
  calc <- eventReactive(input$go, {
    round( #round calculation
      exp(lfactorial(input$sample + input$objects - 1)) / #(n + r -1)!
            (exp(lfactorial(input$sample)) * exp(lfactorial(input$objects - 1))), #r!(n-1)!
      2)
    
  })
  output$combinations <- renderText({
    paste0("There are ", calc(), " possible combinations with replacements")
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Which looks like this…

So there are 5,005 possible password combinations that can unlock my phone. By the way, this is the formula for combinations with replacements (which is included in the server side of the app:

Where n >= 0, and r >= 0. If n = r = 0, then CR(n,r) = 1

Ok this is great, but as of now the app doesn’t follow the rules above:

Adding Validation Message

Let’s provide the user with informative warning messages using the validate() function and use if else statements to satisfy the n = r = 0, then CR(n,r) = 1 rule.

library(shiny)

# Define UI for application
ui <- fluidPage(

    # Application title
    titlePanel("Combination Calculator Without Replacement"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            numericInput("objects",
                        "Number of objects (n)",
                        min = 1,
                        max = 50,
                        step = 1,
                        value = 0),
            numericInput("sample",
                         "Number in sample (r)",
                         min = 1,
                         max = 50,
                         step = 1,
                         value = 0),
            actionButton("go", "Calculate")
        ),

        # Show a plot of the generated distribution
        mainPanel(
          textOutput("combinations")
        )
    )
)

# Define server logic to calculate combinations
server <- function(input, output) {
  calc <- eventReactive(input$go, {
    
    #User input validation
    validate(
      need(input$objects >= 0, "Did not run! Number of objects needs to be equal to or greater than zero"),
      need(input$sample >= 0, "Did not run! Number in sample needs to be equal to or greater than zero")
      )
    
    if( #if statement when objects are greater than 0
      input$objects >0) {
      round( #round calculation
        exp(lfactorial(input$sample + input$objects - 1)) / #(n + r -1)!
          (exp(lfactorial(input$sample)) * exp(lfactorial(input$objects - 1))), #r!(n-1)!
        2)
    }
    else{
      1
    }
    
  })
  output$combinations <- renderText({
    paste0("There are ", calc(), " possible combinations with replacements")
  })
}

# Run the application 
shinyApp(ui = ui, server = server)
## 
## Listening on http://127.0.0.1:8138

Which looks like this…

Nice! You can see that the validation message is triggered when the user inputs values for the objects or samples that are <0 and that our if/else statement worked when the object and sample inputs were both set to 0.

All done!

That’s it for now! A part 2 will be published next month where we will walk through how to test this app (specifically the validation messages) using the shinytest2 package. Stay tuned!

Sarah Narvaiz, PhD
Sarah Narvaiz, PhD
Researcher

My research interests include survey research, psychometrics, and QuantCrit theory.