test-shiny-app
关于
This skill helps developers test Shiny apps using shinytest2 for end-to-end browser testing and testServer() for unit testing module logic. It supports snapshot testing, CI integration, and mocking external services. Use it to add tests to existing apps, establish a testing strategy for new projects, run regression tests before refactoring, or integrate testing into CI/CD pipelines.
快速安装
Claude Code
推荐npx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/test-shiny-app在 Claude Code 中复制并粘贴此命令以安装该技能
技能文档
Test Shiny App
Comprehensive testing: shinytest2 (e2e) + testServer() (unit).
Use When
- Add tests to existing Shiny app
- Set up strategy for new Shiny project
- Regression tests before refactor
- Integrate into CI/CD
In
- Required: Path to Shiny app
- Required: Test scope (unit, e2e, both)
- Optional: Snapshot testing (default: yes for e2e)
- Optional: CI platform (GH Actions, GitLab CI)
- Optional: Modules to test in isolation
Do
Step 1: Install Test Deps
install.packages("shinytest2")
# For golem apps, add as a Suggests dependency
usethis::use_package("shinytest2", type = "Suggests")
# Set up testthat infrastructure if not present
usethis::use_testthat(edition = 3)
Got: shinytest2 installed + testthat dir structure in place.
If err: shinytest2 needs chromote (headless Chrome). Install Chrome/Chromium. WSL: sudo apt install -y chromium-browser. Verify w/ chromote::find_chrome().
Step 2: testServer() Unit Tests
Create tests/testthat/test-mod_dashboard.R:
test_that("dashboard module filters data correctly", {
testServer(dataFilterServer, args = list(
data = reactive(iris),
columns = c("Species", "Sepal.Length")
), {
# Set inputs
session$setInputs(column = "Species")
session$setInputs(value_select = "setosa")
session$setInputs(apply = 1)
# Check output
result <- filtered()
expect_equal(nrow(result), 50)
expect_true(all(result$Species == "setosa"))
})
})
test_that("dashboard module handles empty data", {
testServer(dataFilterServer, args = list(
data = reactive(iris[0, ]),
columns = c("Species")
), {
# Module should not error on empty data
expect_no_error(session$setInputs(column = "Species"))
})
})
Patterns:
testServer()tests module server logic w/o browser- Pass reactive args via
argslist session$setInputs()simulates user- Access reactive returns directly by name
- Test edge: empty data, NULL inputs, invalid values
Got: Module tests pass devtools::test().
If err: testServer() errs "not a module server function" → fn must use moduleServer() internally. session$setInputs() doesn't trigger reactives → add session$flushReact() after.
Step 3: shinytest2 E2E Tests
Create tests/testthat/test-app-e2e.R:
test_that("app loads and displays initial state", {
# For golem apps
app <- AppDriver$new(
app_dir = system.file(package = "myapp"),
name = "initial-load",
height = 800,
width = 1200
)
on.exit(app$stop(), add = TRUE)
# Wait for app to load
app$wait_for_idle(timeout = 10000)
# Check that key elements exist
app$expect_values()
})
test_that("filter interaction updates the table", {
app <- AppDriver$new(
app_dir = system.file(package = "myapp"),
name = "filter-interaction"
)
on.exit(app$stop(), add = TRUE)
# Interact with the app
app$set_inputs(`filter1-column` = "cyl")
app$wait_for_idle()
app$set_inputs(`filter1-apply` = "click")
app$wait_for_idle()
# Snapshot the output values
app$expect_values(output = "table")
})
Patterns:
AppDriver$new()launches in headless Chrome- Always
on.exit(app$stop())for cleanup - Module input IDs:
"moduleId-inputId" app$expect_values()creates/cmps snapshot filesapp$wait_for_idle()ensures reactive updates complete
Got: E2E tests create snapshot files in tests/testthat/_snaps/.
If err: Chrome not found → set CHROMOTE_CHROME env to path. Snapshots fail CI but pass local → platform-dep rendering diffs; use app$expect_values() for data, not app$expect_screenshot() for visual.
Step 4: Record Test Interactively (Optional)
shinytest2::record_test("path/to/app")
Opens app in browser w/ recording panel. Interact, click "Save test" → auto-gen test code.
Got: Test file generated in tests/testthat/ w/ recorded interactions.
If err: Recorder doesn't open → check app runs w/ shiny::runApp() first. Recorder needs working app.
Step 5: Snapshot Mgmt
For snapshot tests, manage expected:
# Accept new/changed snapshots after review
testthat::snapshot_accept("test-app-e2e")
# Review snapshot differences
testthat::snapshot_review("test-app-e2e")
Add snapshot dirs to VCS:
tests/testthat/_snaps/ # Committed — contains expected values
Got: Snapshot files tracked in git for regression detection.
If err: Snapshots change unexpectedly → run testthat::snapshot_review() for diffs. Accept intentional changes w/ testthat::snapshot_accept().
Step 6: CI Integration
Add to .github/workflows/R-CMD-check.yaml | dedicated workflow:
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y chromium-browser
- name: Set Chrome path
run: echo "CHROMOTE_CHROME=$(which chromium-browser)" >> $GITHUB_ENV
- name: Run tests
run: |
Rscript -e 'devtools::test()'
Golem apps → install pkg before testing:
- name: Install app package
run: Rscript -e 'devtools::install()'
Got: Tests pass in CI w/ headless Chrome.
If err: Common: Chrome not installed (add apt-get), display server missing (shinytest2 headless default so usually no issue), timeout on slow runners (↑ timeout in AppDriver$new()).
Check
-
devtools::test()runs all w/o errors - testServer() covers module server logic
- shinytest2 covers key user workflows
- Snapshot files committed
- Tests pass in CI
- Edge cases tested (empty data, NULL inputs, errs)
Traps
- Test UI rendering vs logic: Prefer
testServer()for logic +app$expect_values()for data. Onlyapp$expect_screenshot()when visual matters — screenshots brittle across platforms. - Module ID format e2e: AppDriver uses
"moduleId-inputId"(hyphen), NOT"moduleId.inputId". - Flaky timing: Always
app$wait_for_idle()afterapp$set_inputs(). Without → assertions may run before reactive updates. - Snapshot drift: Don't commit snapshots from diff platforms (Mac vs Linux). Standardize on CI platform.
- Missing Chrome on CI: shinytest2 needs Chrome/Chromium. Always include install step.
→
build-shiny-module— create testable modules w/ clear interfacesscaffold-shiny-app— set up app structure w/ testing infrawrite-testthat-tests— general testthat patterns for R pkgssetup-github-actions-ci— CI/CD setup for R pkgs (golem apps)
GitHub 仓库
相关推荐技能
evaluating-llms-harness
测试该Skill通过60+个学术基准测试(如MMLU、GSM8K等)评估大语言模型质量,适用于模型对比、学术研究及训练进度追踪。它支持HuggingFace、vLLM和API接口,被EleutherAI等行业领先机构广泛采用。开发者可通过简单命令行快速对模型进行多任务批量评估。
cloudflare-cron-triggers
测试这个Claude Skill提供了关于Cloudflare Cron Triggers的完整知识库,用于通过cron表达式定时执行Workers。它支持配置周期性任务、维护作业和自动化工作流,并能处理常见的cron触发错误。开发者可以用它来设置定时任务、测试cron处理器,并集成Workflows和Green Compute功能。
webapp-testing
测试该Skill为开发者提供了基于Playwright的本地Web应用测试工具集,支持自动化测试前端功能、调试UI行为、捕获屏幕截图和查看浏览器日志。它包含管理服务器生命周期的辅助脚本,可直接作为黑盒工具运行而无需阅读源码。适用于需要快速验证本地Web应用界面和交互功能的开发场景。
finishing-a-development-branch
测试这个Skill用于开发分支完成后的集成决策,当代码实现完成且测试通过时,它会引导开发者选择合适的工作流。它首先验证测试状态,然后提供合并、创建PR或清理等结构化选项。核心价值在于确保代码质量的同时,标准化分支收尾流程。
