For R packages that have Shiny applications, there are generally two
ways that the applications will be present in the package. The first is
to have an app.R
in a subdirectory of inst/
.
The second way is to have a function which returns a Shiny app
object.
inst/
An application could live in a subdirectory of inst/
, as
shown below:
/
├── DESCRIPTION
├── NAMESPACE
├── R
├── inst
│ └── sample_app
│ ├── app.R
│ └── tests
│ ├── testthat
│ │ ├── _snaps
│ │ │ └── shinytest2
│ │ │ └── 001.json
│ │ └── test-shinytest2.R
│ └── testthat.R
└── tests
├── testthat
│ └── test-inst-apps.R
└── testthat.R
In this case, you can run record_test()
and
test_app()
as normal. After you create and run the tests,
there will be a tests/
subdirectory in the application
directory that stores the test scripts and results.
Since we are using {testthat}
for automated tests, you
would create a test driver script in tests/testthat/
. In
this example, it’s named test-inst-apps.R
and contains the
following:
# File: tests/testthat/test-inst-apps.R
library(shinytest2)
test_that("sample_app works", {
# Don't run these tests on the CRAN build servers
skip_on_cran()
<- system.file(package = "exPackage", "sample_app")
appdir test_app(appdir)
})
If the application directory is not meant to be public, it can also
be located in ./tests/testthat/apps
.
{shinytest2}
does this with many application and has
appdir
above point to the relative path to the
application.
The second way have an application in an R package is by having a
function that returns a Shiny application object. In this example,
there’s a function hello_world_app()
, which lives in
R/hello-world.R
:
/
├── .Rbuildignore
├── DESCRIPTION
├── NAMESPACE
├── R
│ └── hello-world.R
└── tests
├── testthat
│ ├── _snaps
│ │ └── app-function
│ │ └── 001.json
│ └── test-app-function.R
└── testthat.R
The function simply returns an object from
shinyApp()
:
# File: R/hello-world.R
<- function() {
hello_world_app ::data(cars)
utilsshinyApp(
ui = fluidPage(
sliderInput("n", "n", 1, nrow(cars), 10),
plotOutput("plot")
),server = function(input, output) {
$plot <- renderPlot({
outputplot(head(cars, input$n), xlim = range(cars[[1]]), ylim = range(cars[[2]]))
})
}
) }
Once we have the object, it can be supplied directly to
AppDriver$new()
.
# File: tests/testthat/test-app-function.R
test_that("hello-world app initial values are consistent", {
# Don't run these tests on the CRAN build servers
skip_on_cran()
<- hello_world_app()
shiny_app <- AppDriver$new(shiny_app, name = "hello")
app $expect_values()
app })
To help create tests, you can call record_test()
on your
shiny application object directly. Unfortunately, the test file will not
be able to be saved. Instead, the test commands can be copied into a
test script manually.
There are a few steps that are needed for both types of tests.
It is recommended to call shinytest2::use_shinytest()
to
enable different test config set-ups.
You will need to add {shinytest2}
to the
Suggests
section in your DESCRIPTION
file.
Suggests:
shinytest2
When all of these items are in place, you can test your package using
devtools::install(); testthat::test_local()
or by running
R CMD check
on your package. If you are using the RStudio
IDE, you can also run Build -> Test Package or Build -> Check
Package.
{shinytest2}
requires that your package to be
installed when testing. testthat::test_local()
(and related wrappers) eventually call pkgload::load_all()
to temporarily source the local R package. You can use
test_local()
to test non-{shinytest2}
tests,
but you will need to install your R package to safely execute your
{shinytest2}
tests. If not installed, it will create a
confusing situation where your {shinytest2}
tests are
running on a different version of your R package (whichever was
last installed), than the rest of your tests (the current source).
If you would like your package to be tested with every commit, you can set it up with GitHub Actions. Please see Using shinytest2 with continuous integration for inspiration.