Agent-almanac test-shiny-app
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/de/skills/test-shiny-app" ~/.claude/skills/pjt222-agent-almanac-test-shiny-app-31dfda && rm -rf "$T"
i18n/de/skills/test-shiny-app/SKILL.mdShiny-App testen
Shiny-Anwendungen mit shinytest2 (End-to-End) und testServer() (Unit-Tests) zuverlässig testen.
Wann verwenden
- Shiny-App-Verhalten vor dem Deployment validieren
- Regressionstests für kritische User Journeys einrichten
- Modul-Logik isoliert unit-testen
- Shiny-Tests in GitHub Actions CI einbinden
Eingaben
- Erforderlich: Laufende Shiny-App (lokal oder remote)
- Optional: Bestehende
-Verzeichnisstrukturtests/ - Optional: CI-Workflow für automatische Testausführung
Vorgehensweise
Schritt 1: shinytest2 installieren
shinytest2 und seine Dependencies installieren.
install.packages("shinytest2") # Chromium-Browser für UI-Tests installieren (benötigt Netzwerkzugriff) shinytest2::install_chromium() # Für CI (headless Chrome verwenden) # Chromium wird über chromote automatisch gefunden
In bestehende Teststruktur integrieren:
# Tests-Verzeichnis einrichten (wenn noch nicht vorhanden) usethis::use_testthat() # shinytest2 in Testinfrastruktur integrieren shinytest2::use_shinytest2()
Erwartet: shinytest2 installiert.
tests/testthat/setup-shinytest2.R erstellt.
Bei Fehler: Wenn Chromium-Installation fehlschlägt, systemweit installierten Chrome/Chromium verwenden:
shinytest2::install_chromium(force = TRUE).
Schritt 2: Ersten Snapshot-Test erstellen
shinytest2 im Record-Modus verwenden, um Test-Snapshots automatisch zu generieren.
# Aufzeichnung starten (öffnet App + Test-Recorder) shinytest2::record_test(".") # Alternativ: Manuell ersten Test schreiben # tests/testthat/test-app.R
Aufgezeichnete Tests sehen aus wie:
# tests/testthat/test-app.R library(shinytest2) test_that("app startet und UI rendert korrekt", { app <- AppDriver$new( app_dir = system.file(package = "myapp"), # oder "." name = "app-startup-test" ) # Anfangs-Screenshot aufnehmen app$expect_screenshot() app$stop() })
Erwartet: Snapshot-Bilder in
tests/testthat/_snaps/ erstellt. Test läuft ohne Fehler.
Bei Fehler: Wenn App nicht startet, Konsolen-Output mit
app$get_logs() prüfen. Wenn Screenshot-Vergleich fehlschlägt, Snapshots mit shinytest2::snapshot_review() aktualisieren.
Schritt 3: UI-Interaktionstests schreiben
Tests für spezifisches User-Verhalten schreiben.
# tests/testthat/test-interactions.R library(shinytest2) test_that("Datensatz-Auswahl aktualisiert Tabelle", { app <- AppDriver$new(".", name = "dataset-selection") # Initialen Zustand prüfen initial_value <- app$get_value(output = "table") expect_true(length(initial_value$body) > 0) # Input ändern app$set_inputs(dataset = "mtcars") app$expect_values(output = "table") app$stop() }) test_that("Slider filtert Zeilenanzahl", { app <- AppDriver$new(".", name = "slider-filter") app$set_inputs(n_rows = 5) app$click("apply") # Wert aus Output extrahieren und prüfen table_data <- app$get_value(output = "table") expect_equal(nrow(table_data$body), 5) app$stop() })
Erwartet: Tests simulieren User-Interaktionen und prüfen Output-Änderungen.
Bei Fehler: Wenn
get_value() unerwartete Struktur zurückgibt, app$get_value(output = "table") |> str() verwenden, um die tatsächliche Struktur zu untersuchen.
Schritt 4: Server-Logik unit-testen
Shiny-Server-Funktionen mit
testServer() isoliert testen.
# tests/testthat/test-server.R library(shiny) library(testthat) test_that("server berechnet korrekten Mittelwert", { testServer(app_server, { # Inputs setzen session$setInputs(dataset = "iris", n_rows = 10) # Output prüfen expect_s3_class(output$table$result, "data.frame") expect_equal(nrow(output$table$result), 10) }) }) test_that("reactive Werte aktualisieren korrekt", { testServer(app_server, { session$setInputs(multiplier = 2) # Reaktiven Wert prüfen result <- filtered_data() expect_true(all(result$value > 0)) }) })
Für Modul-Tests:
# tests/testthat/test-mod_data_filter.R test_that("data filter Modul filtert korrekt", { test_data <- reactive({ head(iris, 100) }) testServer( mod_data_filter_server, args = list(data = test_data), { session$setInputs(n_rows = 5, apply = 1) result <- session$returned() expect_equal(nrow(result()), 5) } ) })
Erwartet: Server-Logik unabhängig von UI testbar. Reaktive Berechnungen verifizierbar.
Bei Fehler: Wenn
testServer() fehlschlägt mit "could not find function", sicherstellen, dass Server-Funktion exportiert oder im Paket-Namespace zugänglich ist.
Schritt 5: Edge Cases und Fehlerbehandlung testen
Randfälle und Fehlerszenarien testen.
# tests/testthat/test-edge-cases.R test_that("app behandelt leere Eingaben korrekt", { testServer(app_server, { session$setInputs(dataset = NULL, n_rows = 0) # Sicherstellen dass App nicht abstürzt expect_silent(output$table) }) }) test_that("app zeigt Fehlermeldung bei ungültigen Eingaben", { app <- AppDriver$new(".", name = "error-handling") # Ungültige Eingabe erzwingen (z. B. via URL-Parameter) app$set_inputs(n_rows = -1) app$click("apply") # Fehlermeldung prüfen error_output <- app$get_value(output = "error_message") expect_false(is.null(error_output)) app$stop() })
Erwartet: App behandelt edge cases ohne Absturz. Fehlermeldungen korrekt angezeigt.
Bei Fehler: Wenn Tests edge cases nicht abdecken, Fehlerbehandlung im Server mit
tryCatch() oder validate(need(...)) hinzufügen.
Schritt 6: CI-Integration
Tests in GitHub Actions CI einbinden.
# .github/workflows/test-shiny.yml name: Shiny Tests on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - name: Install system dependencies run: | sudo apt-get install -y chromium-browser - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: shinytest2, testthat - name: Run tests run: | Rscript -e "testthat::test_dir('tests/testthat')" env: CHROMOTE_CHROME_ARGS: "--no-sandbox" # Für CI erforderlich
Erwartet: Tests in CI auf Push/PR ausgeführt. Fehlgeschlagene Tests blockieren Merge.
Bei Fehler: Wenn Chromium in CI nicht gefunden wird,
CHROMOTE_CHROME_ARGS="--no-sandbox --disable-gpu" und sicherstellen, dass chromium-browser oder google-chrome installiert ist.
Validierung
- shinytest2 installiert und Chromium verfügbar
- Snapshots in
erstellttests/testthat/_snaps/ - UI-Interaktionstests simulieren echte User-Workflows
- Server-Unit-Tests via
prüfen Logik isolierttestServer() - Edge Cases und Fehlerbehandlung getestet
- Tests in CI laufen ohne Fehler
Haeufige Stolperfallen
- Nicht-deterministische Snapshots: Animationen, Timestamps oder zufällige Daten verursachen Snapshot-Instabilität.
verwenden und dynamischen Inhalt mocken.set.seed() - Chromium fehlt in CI: Immer
-Flag in CI-Umgebungen setzen. GitHub Actions braucht spezielle Chromium-Konfiguration.--no-sandbox
vstestServer()
:AppDriver
testet Server-Logik isoliert (keine echten UI).testServer()
testet echte Browser-Interaktionen.AppDriver- Veraltete Snapshots: Nach UI-Änderungen müssen Snapshots mit
odershinytest2::snapshot_review()
aktualisiert werden.shinytest2::snapshot_update() - Timing-Probleme: Asynchrone Operationen (Plots, Downloads) benötigen
nach auslösenden Aktionen.app$wait_for_idle() - Modul-Tests ohne Reaktivität:
benötigt reaktive Inputs — immertestServer()
vor Assertions aufrufen.session$setInputs()
Verwandte Skills
— Shiny-App vor dem Testen scaffoldenscaffold-shiny-app
— Module erstellen, die mitbuild-shiny-module
getestet werdentestServer()
— Getestete App deployendeploy-shiny-app
— CI/CD-Workflows einrichtensetup-github-actions-ci