mirror of
https://github.com/rstudio/shiny.git
synced 2026-02-11 23:25:00 -05:00
96 lines
3.3 KiB
Plaintext
96 lines
3.3 KiB
Plaintext
---
|
|
title: "Integration Testing in Shiny"
|
|
output: rmarkdown::html_vignette
|
|
vignette: >
|
|
%\VignetteEngine{knitr::rmarkdown}
|
|
%\VignetteIndexEntry{Your Vignette Title}
|
|
%\VignetteEncoding{UTF-8}
|
|
---
|
|
|
|
```{r setup, include=FALSE}
|
|
knitr::opts_chunk$set(echo = TRUE)
|
|
```
|
|
|
|
## Introduction to Inspecting Modules
|
|
|
|
First, we'll define a simple Shiny module:
|
|
|
|
```{r}
|
|
library(shiny)
|
|
module <- function(input, output, session) {
|
|
rv <- reactiveValues(x = 0)
|
|
observe({
|
|
rv$x <- input$x * 2
|
|
})
|
|
output$txt <- renderText({
|
|
paste0("Value: ", rv$x)
|
|
})
|
|
}
|
|
```
|
|
|
|
This module
|
|
|
|
- depends on one input (`x`)
|
|
- has an intermediate, internal `reactiveValues` (`rv`) which updates reactively
|
|
- and updates an output (`txt`) reactively.
|
|
|
|
It would be nice to write tests that confirm that the module behaves the way we expect. We can do so using the `testModule` function.
|
|
|
|
```{r}
|
|
testModule(module, {
|
|
cat("Initially, input$x is NULL, right?", is.null(input$x), "\n")
|
|
|
|
# Give input$x a value.
|
|
input$x <- 1
|
|
|
|
cat("Now that x is set to 1, rv$x is: ", rv$x, "\n")
|
|
cat("\tand output$txt is: ", output$txt(), "\n")
|
|
|
|
# Now update input$x to a new value
|
|
input$x <- 2
|
|
|
|
cat("After updating x to 2, rv$x is: ", rv$x, "\n")
|
|
cat("\tand output$txt is: ", output$txt(), "\n")
|
|
})
|
|
```
|
|
|
|
There are a few things to notice in this example.
|
|
|
|
First, the test expression provided here assumes the existence of some variables -- specifically, `input`, `output`, and `r`. This is safe because the test code provided to `testModule` is run in the module's environment. This means that any parameters passed in to your module (such as `input`, `output`, and `session`) are readily available, as are any intermediate objects or reactives that you define in the module (such as `r`).
|
|
|
|
Second, you'll need to give values to any inputs that you want to be defined; by default, they're all `NULL`. You can either do this in-line as we've done above, or by specifying a value for the `initialState` parameter of the `testModule` function.
|
|
|
|
Last, you may have noticed that we have parenthesis when printing `output$txt`. You're probably used to referencing outputs without parenthesis, but outputs are actually `<shiny.render.function>`s so you'll need to evaluate them in order to compare them.
|
|
|
|
## Automated Tests
|
|
|
|
Realistically, you don't want to just print the values for manual inspection, you'll want to leverage them in automated tests. That way, we'll be able to build up a collection of tests that we can run against our module in the future to confirm that it always behaves correctly. You can use whatever testing framework you'd like (or none a all!), but we'll use the `expect_*` functions from the testthat package in this example.
|
|
|
|
```{r}
|
|
# Bring in testthat just for its expectations
|
|
suppressWarnings(library(testthat))
|
|
|
|
# Specify an initial value of x=1 in the `initialState` parameter below
|
|
testModule(module, {
|
|
expect_equal(rv$x, 2)
|
|
expect_equal(output$txt(), "Value: 2")
|
|
|
|
input$x <- 2
|
|
expect_equal(rv$x, 4)
|
|
expect_equal(output$txt(), "Value: 4")
|
|
}, initialState = list(x=1))
|
|
```
|
|
|
|
If there's no error, then we know our tests ran successfully. If there were a bug, we'd see an error printed. For example:
|
|
|
|
```{r}
|
|
tryCatch({
|
|
testModule(module, {
|
|
expect_equal(rv$x, 99)
|
|
}, initialState = list(x=1))
|
|
}, error=function(e){
|
|
print("There was an error!")
|
|
print(e)
|
|
})
|
|
```
|