Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Modules tested with testServer
run with a session that is a MockShinySession
object.
For most cases we only need:
MockShinySession$setInputs()
method which simulates users interactions with widgets.MockShinySession$returned
which contains the value returned by the module.
Let’s take a look an example.
We develop a module that takes a dataset as a parameter, and returns a subset of the data based on the selected variables in a dropdown.
describe("server", { it("should subset the data with selected variables", { # Arrange # Act # Assert }) })
We arrange parameters to pass to the module.
describe("server", { it("should subset the data with selected variables", { # Arrange args <- list(data = iris) # Act # Assert }) })
Arguments are passed to the module as a list.
describe("server", { it("should subset the data with selected variables", { # Arrange args <- list(data = iris) shiny::testServer(server, args = args, { # Act # Act # Assert # Assert }) }) })
Code within testServer
has access to the session, inputs and outputs. We select 2 variables using an input that has "select"
ID. We assume that we will implement an input with this ID.
describe("server", { it("should subset the data with selected variables", { # Arrange args <- list(data = iris) shiny::testServer(server, args = args, { # Act session$setInputs(select = c("Sepal.Length", "Species")) # Assert }) }) })
The return value should be a column subset of the data
. We use session$returned()
to get the value of the returned reactive
.
describe("server", { it("should subset the data with selected variables", { # Arrange args <- list(data = iris) shiny::testServer(server, args = args, { # Act session$setInputs(select = c("Sepal.Length", "Species")) # Assert expect_equal( colnames(session$returned()), c("Sepal.Length", "Species") ) }) }) })
To use shiny::testServer
the module must be implemented with shiny::moduleServer
.
server <- function(id) { moduleServer(id, function(input, output, session) { # ... }) }
And this is a module that passes this test.
ui <- function(id) { ns <- NS(id) fluidPage( selectInput(ns("select"), "Select variables", choices = NULL, multiple = TRUE), ) } server <- function(id, data) { moduleServer(id, function(input, output, session) { updateSelectInput(session, "select", choices = colnames(data)) return(reactive(data[, input$select])) }) }
Notice how we don’t check the UI in this test. Using this test we don’t know if the module updated the input correctly.
Use shiny::testServer
to test low level behaviors of the module.
Use it to test contracts:
- if it returns the correct value,
- or if it runs a side effect.
This might be especially useful if this is a “low-level” module that is used in many places in the app.
Use {shinytest2}
to test both parts of a Shiny module.
Using {shinytest2}
can be better to test user behaviors by simulating real interactions with the app.
Writing tests first for Shiny modules helps to keep them:
- simple,
- focused,
- and doing exactly what they need to do.
Tests help define what should be the input to the module:
- what it should do,
- and what it should return.
Such modules are easier to reuse and easier to compose them to build the whole app.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.