Unit-Tests für Skills

Nachdem Sie einen Skill geschrieben haben, wie können Sie bestätigen, dass er tatsächlich wie erwartet funktioniert? Manuelles Testen ist zeitaufwändig und fehleranfällig.

Dieser Artikel zeigt, wie Sie systematische Testfälle für einen Skill erstellen, vom Skript-Unit-Testing bis zum End-to-End-Trigger-Testing.


Zwei Ebenen des Skill-Testings

StufeTestzielMethode
Skript-Unit-TestsPython/JS-Skripte unter scripts/pytest / Standard-unittest
Trigger-basiertes End-to-End-TestingOb der Skill korrekt ausgelöst wird und ob die Ausgabe die Erwartungen erfülltEvaluierungsframework von skill-creator

Stufe 1: Skript-Unit-Tests

Skripte sind der deterministischste Teil eines Skills und eignen sich gut für die Abdeckung mit einem standardmäßigen Unit-Test-Framework.

Beispiel

# Dateipfad: scripts/tests/test_clean_data.py
import pytest
import os
import tempfile
import csv

# Zu testendes Modul
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from clean_data import remove_duplicates, fill_nulls, strip_whitespace

# ── Test: Deduplizierung ──────────────────────────────────────────
def test_remove_duplicates_basic():
    """Normalfall: doppelte Zeilen sollten entfernt werden"""
    rows = [
        {"name": "example", "score": "90"},
        {"name": "example", "score": "90"},   # Doppelte Zeile
        {"name": "EXAMPLE", "score": "80"},
    ]
    result = remove_duplicates(rows)
    assert len(result) == 2, "1 doppelte Zeile sollte entfernt werden"

def test_remove_duplicates_empty():
    """Randfall: leere Liste sollte keinen Fehler auslösen"""
    result = remove_duplicates([])
    assert result == []

# ── Test: Füllen von Nullwerten ──────────────────────────────────────
def test_fill_nulls_numeric():
    """Nullwerte in numerischen Spalten sollten mit 0 gefüllt werden"""
    rows = [{"value": "10"}, {"value": ""}, {"value": "20"}]
    result = fill_nulls(rows, col="value", fill_with="0")
    assert result[1]["value"] == "0"

# ── Test: Leerzeichen entfernen ──────────────────────────────────────
def test_strip_whitespace():
    """Führende und nachfolgende Leerzeichen in Zeichenketten sollten entfernt werden"""
    rows = [{"name": " example "}, {"name": "BEISPIEL"}]
    result = strip_whitespace(rows, col="name")
    assert result[0]["name"] == "example"
    assert result[1]["name"] == "EXAMPLE"

# ── Integrationstest: Lesen einer echten Datei ──────────────────────────────
def test_process_real_file():
    """Erstelle eine temporäre CSV-Datei und teste den vollständigen Verarbeitungsablauf"""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".csv",
                                    delete=False, newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["name", "score"])
        writer.writeheader()
        writer.writerow({"name": " example ", "score": "90"})
        writer.writerow({"name": " example ", "score": "90"})  # Duplikat
        writer.writerow({"name": "EXAMPLE",     "score": ""})    # Nullwert
        tmp_path = f.name

    try:
        from clean_data import process_file
        result = process_file(tmp_path)
        assert result["removed_rows"] == 1
        assert result["fixed_values"] == 1
    finally:
        os.unlink(tmp_path)

Tests ausführen:

# 进入 Skill 目录后运行所有测试
cd my-skill/
pytest scripts/tests/ -v

# 生成覆盖率报告
pytest scripts/tests/ --cov=scripts --cov-report=term-missing

Ausgabe:

scripts/tests/test_clean_data.py::test_remove_duplicates_basic  PASSED
scripts/tests/test_clean_data.py::test_remove_duplicates_empty  PASSED
scripts/tests/test_clean_data.py::test_fill_nulls_numeric       PASSED
scripts/tests/test_clean_data.py::test_strip_whitespace         PASSED
scripts/tests/test_clean_data.py::test_process_real_file        PASSED

5 passed in 0.12s

Stufe 2: Trigger-basiertes End-to-End-Testing

Trigger-Tests überprüfen, ob Claude den Skill verwendet, wenn ein Benutzer eine Anfrage stellt.

Testfälle werden im JSON-Format definiert und enthalten Benutzer-Prompts sowie Assertions zum erwarteten Verhalten.

Beispiel

[
  {
    "id": "trigger_basic",
    "prompt": "Hilf mir, diese Verkaufsdaten-CSV zu analysieren, monatliche Trends zu finden und eine statistische Zusammenfassung zu erstellen",
    "assertions": [
      {
        "type": "skill_triggered",
        "skill": "csv-analyzer",
        "description": "Komplexe Analyseanfragen sollten csv-analyzer auslösen"
      }
    ]
  },
  {
    "id": "trigger_with_file",
    "prompt": "Ich habe example_sales.csv hochgeladen, bitte analysiere die Datenverteilung jeder Spalte",
    "assertions": [
      {
        "type": "skill_triggered",
        "skill": "csv-analyzer"
      },
      {
        "type": "output_contains",
        "keyword": "Statistik",
        "description": "Die Ausgabe sollte statistische Informationen enthalten"
      }
    ]
  },
  {
    "id": "no_trigger_simple",
    "prompt": "Welches Format hat CSV?",
    "assertions": [
      {
        "type": "skill_not_triggered",
        "skill": "csv-analyzer",
        "description": "Einfache Wissensfragen sollten den Skill nicht auslösen"
      }
    ]
  }
]

Trigger-Tests ausführen (erfordert das von skill-creator bereitgestellte Eval-Tool):

Beispiel

# Testsuite ausführen und einen visuellen Bericht erstellen
python -m scripts.run_eval \
  --eval-set evals/trigger-eval.json \
  --skill-path csv-analyzer/ \
  --model claude-sonnet-4-20250514

# Einen HTML-Bericht zur manuellen Überprüfung erstellen
python eval-viewer/generate_review.py \
  --results evals/results/ \
  --output evals/review.html

Testfall-Prompts sollten ausreichend komplex sein. Bei einfachen Anfragen wie "CSV lesen" bedeutet selbst ein fehlgeschlagenes Auslösen nicht, dass der Skill ein Problem hat, denn Claude kann dies selbst bewältigen. Gute Testfälle sollten mehrstufige Anfragen sein, die Nutzer tatsächlich stellen würden, mit klaren Ausgabeanforderungen.


Testfallabdeckung

TypeAbzudeckende Szenarien
Positives AuslösenKomplexe Aufgaben mit Schlüsselwörtern und klaren Anforderungen an das Ausgabeformat
Negatives AuslösenEinfache Q&A, bei denen relevante Begriffe nur erwähnt werden, der Skill aber nicht benötigt wird
GrenzeingabenLeere Dateien, übergroße Dateien, Dateien mit nicht unterstützten Formaten
FehlerwiederherstellungOb die Fehlermeldung klar ist, wenn der Dateipfad falsch ist
Weitere Erweiterungen