Skip to content
Back to skills

Test Shiny App

ASecurity

Shiny-Anwendungen mit shinytest2 für End-to-End-Tests und testServer() für Unit-Tests der Server-Logik testen. Behandelt Snapshot-Tests, UI-Interaktionstests, Modul-Unit-Tests und CI-Integration. Verwenden, wenn Shiny-App-Verhalten validiert, Regressionstests eingerichtet oder Tests in CI/CD eingebunden werden sollen.

  • 31 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 29, 2026
ai-agentsgobashreacttestinggitci/cd

Works with

  • cli

Security analysis

A100/100

Scanned May 29, 2026

npx -y skills add pjt222/agent-almanac --skill test-shiny-app --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Shiny App?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Shiny App
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pjt222-test-shiny-app/badge)](https://www.skillsdirectory.com/skills/pjt222-test-shiny-app)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-shiny-app
description: >
  Shiny-Anwendungen mit shinytest2 für End-to-End-Tests und testServer()
  für Unit-Tests der Server-Logik testen. Behandelt Snapshot-Tests,
  UI-Interaktionstests, Modul-Unit-Tests und CI-Integration. Verwenden,
  wenn Shiny-App-Verhalten validiert, Regressionstests eingerichtet oder
  Tests in CI/CD eingebunden werden sollen.
license: MIT
locale: de
source_locale: en
source_commit: 6f65f316
translator: claude-opus-4-6
translation_date: 2026-03-16
allowed-tools: Read Write Edit Bash Grep Glob
metadata:
  author: Philipp Thoss
  version: "1.0"
  domain: shiny
  complexity: intermediate
  language: R
  tags: shiny, testing, shinytest2, testthat, ci-cd
---

# Shiny-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 `tests/`-Verzeichnisstruktur
- **Optional**: CI-Workflow für automatische Testausführung

## Vorgehensweise

### Schritt 1: shinytest2 installieren

shinytest2 und seine Dependencies installieren.

```r
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:

```r
# 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.

```r
# Aufzeichnung starten (öffnet App + Test-Recorder)
shinytest2::record_test(".")

# Alternativ: Manuell ersten Test schreiben
# tests/testthat/test-app.R
```

Aufgezeichnete Tests sehen aus wie:

```r
# 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.

```r
# 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.

```r
# 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:

```r
# 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.

```r
# 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.

```yaml
# .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 `tests/testthat/_snaps/` erstellt
- [ ] UI-Interaktionstests simulieren echte User-Workflows
- [ ] Server-Unit-Tests via `testServer()` prüfen Logik isoliert
- [ ] 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. `set.seed()` verwenden und dynamischen Inhalt mocken.
- **Chromium fehlt in CI**: Immer `--no-sandbox`-Flag in CI-Umgebungen setzen. GitHub Actions braucht spezielle Chromium-Konfiguration.
- **`testServer()` vs `AppDriver`**: `testServer()` testet Server-Logik isoliert (keine echten UI). `AppDriver` testet echte Browser-Interaktionen.
- **Veraltete Snapshots**: Nach UI-Änderungen müssen Snapshots mit `shinytest2::snapshot_review()` oder `shinytest2::snapshot_update()` aktualisiert werden.
- **Timing-Probleme**: Asynchrone Operationen (Plots, Downloads) benötigen `app$wait_for_idle()` nach auslösenden Aktionen.
- **Modul-Tests ohne Reaktivität**: `testServer()` benötigt reaktive Inputs — immer `session$setInputs()` vor Assertions aufrufen.

## Verwandte Skills

- `scaffold-shiny-app` — Shiny-App vor dem Testen scaffolden
- `build-shiny-module` — Module erstellen, die mit `testServer()` getestet werden
- `deploy-shiny-app` — Getestete App deployen
- `setup-github-actions-ci` — CI/CD-Workflows einrichten

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…