我有一个 R 包,可以向用户提供
learnr
教程。这些教程本质上是闪亮的应用程序。准确地说,它们是 runtime: shiny_prerendered
交互式 R Markdown 文档。
在开发新教程和维护旧教程时,有时我和我的团队会在一些教程中引入错误。我想通过 GitHub Actions 进行持续集成,它可以简单地检查包中的所有现有教程是否可以无错误地呈现。
我已经可以在 GitHub Actions 上运行任意 R 代码了,但显然这不是交互式会话。我知道如何在交互式上下文中呈现教程,但如何在非交互式上下文中呈现它们?并报告哪些教程渲染失败?
我也听说过闪亮应用程序的单元测试,但我不知道如何将其应用于我的情况。我的单元测试类似于“测试
learnr::run_tutorial("path/to/tutorial")
没有错误。
当这个函数运行时,它会自动处理很多东西,但我不知道它在非交互式会话中如何工作。例如,在渲染并成功运行教程闪亮进程后,最好停止它。
下面是我在 `.github/workflows 中的 YAML 文件:
on:
push:
branches: main
jobs:
import-data:
runs-on: ubuntu-latest
steps:
- name: Set up R
uses: r-lib/actions/setup-r@v2
- name: Install packages
uses: r-lib/actions/setup-r-dependencies@v2
with:
packages: |
any::tidyverse
any::renv
any::learnr
any::shiny
any::rmarkdown
any::rcmdcheck
any::rlang
- name: Check out repository
uses: actions/checkout@v3
- name: Test render of tutorials
run: Rscript -e 'source("./.github/rscripts/tutorial_action.R")'
这是我在最后一步中运行的 R 脚本:
# Build and install the local package
devtools::install()
# 1. Install necessary dependencies
deps <- renv::dependencies("./inst/tutorials")$Package
installed <- installed.packages()
to_install <- deps[!(deps %in% installed)]
lapply(to_install, install.packages)
# 2. Get all possible tutorial paths
tutorial_paths <- list.files("inst/tutorials", recursive = TRUE)
tutorial_paths <- tutorial_paths[endsWith(tutorial_paths, ".Rmd")]
tutorial_paths <- tutorial_paths[!is.na(tutorial_paths)]
tutorial_paths <- paste0("inst/tutorials/", tutorial_paths)
# 3. Try to test the tutorials
options(shiny.testmode = TRUE)
for (i in seq_len(length(tutorial_paths))) {
learnr::run_tutorial(tutorial_paths[i],
as_rstudio_job = FALSE)
shiny::stopApp()
}
一切正常,直到第 3 点,我只是随机尝试了一些东西,希望它能起作用。
编辑:典型教程的最小示例
---
title: "Tutorial"
output: learnr::tutorial
runtime: shiny_prerendered
---
```{r setup, include=FALSE}
library(learnr)
knitr::opts_chunk$set(echo = FALSE)
```
## Topic 1
### Exercise
*Here's a simple exercise with an empty code chunk provided for entering the answer.*
Write the R code required to add two plus two:
```{r two-plus-two, exercise=TRUE}
```
您应该首先在本地设置单元测试环境,并能够在本地计算机上成功运行(单元)测试。首先,为一个将成功通过的应用程序创建一个测试。然后,您应该为每个应用程序创建测试。 一种方法是使用shinytest2包。您应该为此安装 Chrome 浏览器。
步骤 1. 您的包应严格遵循 R 包的约定。请参阅此处了解更多信息。运行
devtools::load_all()
应该可以没有错误。值得注意的是,R/
文件夹中的文件应仅包含函数,并且在获取它们时不应出现问题。您至少应该安装软件包 devtools
和 testthat
。
第 2 步。 设置
shinytest2
。运行以下命令:
install.packages("shinytest2")
# to add the package to your dependency:
usethis::use_package("shinytest2", type = "Suggests")
# For creating local temporary files that are deleted after testing:
usethis::use_package("withr", type = "Suggests")
# setup testthat:
usethis::use_testthat()
# setup shinytest2:
shinytest2::use_shinytest2()
#create your first test:
shinytest2::use_shinytest2_test()
如果您的包名为“testlearnr”,您的包文件夹现在应该看起来像这样:
├── DESCRIPTION
├── inst
│ └── tutorial1.Rmd
├── man
│ └── hello.Rd
├── NAMESPACE
├── R
├── testlearnr.Rproj
└── tests
├── testthat
│ ├── fixtures
│ ├── setup-shinytest2.R
│ ├── test-shinytest2.R
└── testthat.R
步骤3.修改测试文件
test-shinytest2.R
。
添加如下所述的应用程序驱动程序以进行最小测试(将包名称“testlearnr”更改为您自己的包名称)。
library(shinytest2)
test_that("app1 works", {
temp_file <- withr::local_tempfile(fileext = "temp_testapp.rmd")
app_location <- system.file("tutorial1.Rmd", package = "testlearnr")
file.copy(app_location, temp_file)
app <- AppDriver$new(
app_dir = temp_file,
name = "app-feature-1",
width = 1619,
height = 955
)
app$wait_for_idle()
app$expect_values()
})
步骤 4. 使用
devtools::test_active_file()
运行测试。迭代。确保应用程序在测试中正常运行。在测试中使用 browser()
进行交互式调试。使用 app$view()
以交互方式查看应用程序。这应该会打开一个无头 Chrome 浏览器,并且应用程序成功运行。您应该在这里定义您的正式测试。 app$expect_values()
是一个通用语句,用于保存应用程序状态的某种快照。运行一次后,快照被保存。下次测试应该会成功通过。
这应该可以帮助您开始。一旦您能够完成所有这些步骤,您就可以根据需要经常运行测试。请阅读此处的信息以获取更多信息。