Communitygithub.com

Perl-Testmuster

Perl-Testmuster mit Test2::V0, Test::More, prove-Ausführung, Mocking, Codeabdeckung mit Devel::Cover und TDD-Methodik. Verwenden Sie dies beim Schreiben von Perl-Tests mit Test2::V0 oder Test::More oder beim Messen der Abdeckung.

Was ist Perl-Testmuster?

Dieses Skill behandelt umfassende Teststrategien für Perl-Anwendungen unter Verwendung moderner Frameworks wie Test2::V0 und Test::More sowie des Standard-Test-Ausführers prove. Es betont die TDD-Methodik (Red-Green-Refactor) und bietet detaillierte Muster zum Schreiben von Tests, zum Organisieren von Testverzeichnissen, zum Mocken von Abhängigkeiten und zum Messen der Codeabdeckung mit Devel::Cover. Das Skill unterstützt Entwickler bei der Einrichtung einer zuverlässigen Testinfrastruktur, der Migration von Test::More zu Test2::V0 und der Fehlersuche bei fehlschlagenden Tests. Es enthält praktische Beispiele für tiefe Vergleiche, Subtests, Ausnahmetests und bewährte Verfahren, um Tests schnell, fokussiert und unabhängig zu halten. Empfohlen für die Arbeit an Perl-Projekten mit Claude Code, Cursor, Codex oder anderen KI-Agenten, bei denen eine gründliche Testabdeckung und saubere Testmuster entscheidend sind.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/perl-testing

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Perl-Testmuster?

Umfassende Teststrategien für Perl-Anwendungen mit Test2::V0, Test::More, prove und TDD-Methodik.

Wann aktivieren

  • Beim Schreiben von neuem Perl-Code (TDD befolgen: rot, grün, refaktorisieren)
  • Beim Entwerfen von Testsuiten für Perl-Module oder -Anwendungen
  • Beim Überprüfen der Perl-Testabdeckung
  • Beim Einrichten der Perl-Testinfrastruktur
  • Beim Migrieren von Tests von Test::More zu Test2::V0
  • Beim Debuggen fehlschlagender Perl-Tests

TDD-Workflow

Befolgen Sie immer den Rot-Grün-Refaktorisieren-Zyklus.

# Schritt 1: ROT – Schreiben Sie einen fehlschlagenden Test
# t/unit/calculator.t
use v5.36;
use Test2::V0;

use lib 'lib';
use Calculator;

subtest 'addition' => sub {
    my $calc = Calculator->new;
    is($calc->add(2, 3), 5, 'addiert zwei Zahlen');
    is($calc->add(-1, 1), 0, 'behandelt negative Zahlen');
};

done_testing;

# Schritt 2: GRÜN – Minimale Implementierung schreiben
# lib/Calculator.pm
package Calculator;
use v5.36;
use Moo;

sub add($self, $a, $b) {
    return $a + $b;
}

1;

# Schritt 3: REFAKTORISIEREN – Verbessern, während Tests grün bleiben
# Ausführen: prove -lv t/unit/calculator.t

Grundlagen von Test::More

Das Standard-Perl-Testmodul – weit verbreitet, im Core enthalten.

Grundlegende Assertions

use v5.36;
use Test::More;

# Plan vorher oder done_testing verwenden
# plan tests => 5;  # Fester Plan (optional)

# Gleichheit
is($result, 42, 'gibt korrekten Wert zurück');
isnt($result, 0, 'nicht Null');

# Boolean
ok($user->is_active, 'Benutzer ist aktiv');
ok(!$user->is_banned, 'Benutzer ist nicht gesperrt');

# Tiefenvergleich
is_deeply(
    $got,
    { name => 'Alice', roles => ['admin'] },
    'gibt erwartete Struktur zurück'
);

# Mustervergleich
like($error, qr/nicht gefunden/i, 'Fehler erwähnt "nicht gefunden"');
unlike($output, qr/passwort/, 'Ausgabe versteckt Passwort');

# Typ-Prüfung
isa_ok($obj, 'MyApp::User');
can_ok($obj, 'save', 'delete');

done_testing;

SKIP und TODO

use v5.36;
use Test::More;

# Tests bedingt überspringen
SKIP: {
    skip 'Keine Datenbank konfiguriert', 2 unless $ENV{TEST_DB};

    my $db = connect_db();
    ok($db->ping, 'Datenbank ist erreichbar');
    is($db->version, '15', 'korrekte PostgreSQL-Version');
}

# Erwartete Fehlschläge markieren
TODO: {
    local $TODO = 'Caching noch nicht implementiert';
    is($cache->get('key'), 'value', 'Cache gibt Wert zurück');
}

done_testing;

Test2::V0 Modernes Framework

Test2::V0 ist der moderne Ersatz für Test::More – reichhaltigere Assertions, bessere Diagnostik und erweiterbar.

Warum Test2?

  • Überlegener Tiefenvergleich mit Hash-/Array-Buildern
  • Bessere Diagnoseausgabe bei Fehlschlägen
  • Subtests mit saubererem Gültigkeitsbereich
  • Erweiterbar über Test2::Tools::*-Plugins
  • Abwärtskompatibel mit Test::More-Tests

Tiefenvergleich mit Buildern

use v5.36;
use Test2::V0;

# Hash-Builder – Teil-Struktur prüfen
is(
    $user->to_hash,
    hash {
        field name  => 'Alice';
        field email => match(qr/\@example\.com$/);
        field age   => validator(sub { $_ >= 18 });
        # Andere Felder ignorieren
        etc();
    },
    'Benutzer hat erwartete Felder'
);

# Array-Builder
is(
    $result,
    array {
        item 'first';
        item match(qr/^second/);
        item DNE();  # Does Not Exist – überprüft, dass keine zusätzlichen Elemente vorhanden
    },
    'Ergebnis entspricht erwarteter Liste'
);

# Bag – Reihenfolge-unabhängiger Vergleich
is(
    $tags,
    bag {
        item 'perl';
        item 'testing';
        item 'tdd';
    },
    'hat alle erforderlichen Tags unabhängig von der Reihenfolge'
);

Subtests

use v5.36;
use Test2::V0;

subtest 'Benutzererstellung' => sub {
    my $user = User->new(name => 'Alice', email => '[email protected]');
    ok($user, 'Benutzerobjekt erstellt');
    is($user->name, 'Alice', 'Name ist gesetzt');
    is($user->email, '[email protected]', 'E-Mail ist gesetzt');
};

subtest 'Benutzervalidierung' => sub {
    my $warnings = warns {
        User->new(name => '', email => 'bad');
    };
    ok($warnings, 'warnt bei ungültigen Daten');
};

done_testing;

Ausnahmetesting mit Test2

use v5.36;
use Test2::V0;

# Testen, dass Code stirbt
like(
    dies { divide(10, 0) },
    qr/Division durch Null/,
    'stirbt bei Division durch Null'
);

# Testen, dass Code lebt
ok(lives { divide(10, 2) }, 'Division erfolgreich') or note($@);

# Kombiniertes Muster
subtest 'Fehlerbehandlung' => sub {
    ok(lives { parse_config('gueltig.json') }, 'gültige Konfiguration wird geparst');
    like(
        dies { parse_config('fehlt.json') },
        qr/Kann nicht öffnen/,
        'fehlende Datei stirbt mit Meldung'
    );
};

done_testing;

Testorganisation und prove

Verzeichnisstruktur

t/
├── 00-load.t              # Überprüft, ob Module kompilieren
├── 01-basic.t             # Kernfunktionalität
├── unit/
│   ├── config.t           # Unit-Tests pro Modul
│   ├── user.t
│   └── util.t
├── integration/
│   ├── database.t
│   └── api.t
├── lib/
│   └── TestHelper.pm      # Gemeinsame Test-Hilfsprogramme
└── fixtures/
    ├── config.json        # Testdatendateien
    └── users.csv

prove-Befehle

# Alle Tests ausführen
prove -l t/

# Ausführliche Ausgabe
prove -lv t/

# Bestimmten Test ausführen
prove -lv t/unit/user.t

# Rekursive Suche
prove -lr t/

# Parallele Ausführung (8 Jobs)
prove -lr -j8 t/

# Nur fehlgeschlagene Tests aus dem letzten Lauf ausführen
prove -l --state=failed t/

# Farbige Ausgabe mit Timer
prove -l --color --timer t/

# TAP-Ausgabe für CI
prove -l --formatter TAP::Formatter::JUnit t/ > results.xml

.proverc-Konfiguration

-l
--color
--timer
-r
-j4
--state=save

Fixtures und Setup/Teardown

Subtest-Isolation

use v5.36;
use Test2::V0;
use File::Temp qw(tempdir);
use Path::Tiny;

subtest 'Dateiverarbeitung' => sub {
    # Setup
    my $dir = tempdir(CLEANUP => 1);
    my $file = path($dir, 'eingabe.txt');
    $file->spew_utf8("zeile1\nzeile2\nzeile3\n");

    # Test
    my $result = process_file("$file");
    is($result->{line_count}, 3, 'zählt Zeilen');

    # Teardown erfolgt automatisch (CLEANUP => 1)
};

Gemeinsame Test-Helfer

Platzieren Sie wiederverwendbare Helfer in t/lib/TestHelper.pm und laden Sie sie mit use lib 't/lib'. Exportieren Sie Factory-Funktionen wie create_test_db(), create_temp_dir() und fixture_path() über Exporter.

Mocking

Test::MockModule

use v5.36;
use Test2::V0;
use Test::MockModule;

subtest 'Externe API mocken' => sub {
    my $mock = Test::MockModule->new('MyApp::API');

    # Gut: Mock gibt kontrollierte Daten zurück
    $mock->mock(fetch_user => sub ($self, $id) {
        return { id => $id, name => 'Mock-Benutzer', email => '[email protected]' };
    });

    my $api = MyApp::API->new;
    my $user = $api->fetch_user(42);
    is($user->{name}, 'Mock-Benutzer', 'gibt gemockten Benutzer zurück');

    # Aufrufanzahl überprüfen
    my $call_count = 0;
    $mock->mock(fetch_user => sub { $call_count++; return {} });
    $api->fetch_user(1);
    $api->fetch_user(2);
    is($call_count, 2, 'fetch_user wurde zweimal aufgerufen');

    # Mock wird automatisch wiederhergestellt, wenn $mock den Gültigkeitsbereich verlässt
};

# Schlecht: Monkey-Patching ohne Wiederherstellung
# *MyApp::API::fetch_user = sub { ... };  # NIEMALS – leckt über Tests hinweg

Für leichte Mock-Objekte verwenden Sie Test::MockObject, um injizierbare Test-Doubles mit ->mock() zu erstellen und Aufrufe mit ->called_ok() zu überprüfen.

Abdeckung mit Devel::Cover

Abdeckung ausführen

# Basis-Abdeckungsbericht
cover -test

# Oder Schritt für Schritt
perl -MDevel::Cover -Ilib t/unit/user.t
cover

# HTML-Bericht
cover -report html
open cover_db/coverage.html

# Bestimmte Schwellenwerte
cover -test -report text | grep 'Total'

# CI-freundlich: Fehlschlag bei Unterschreitung des Schwellenwerts
cover -test && cover -report text -select '^lib/' \
  | perl -ne 'if (/Total.*?(\d+\.\d+)/) { exit 1 if $1 < 80 }'

Integrationstests

Verwenden Sie In-Memory-SQLite für Datenbanktests, mocken Sie HTTP::Tiny für API-Tests.

use v5.36;
use Test2::V0;
use DBI;

subtest 'Datenbankintegration' => sub {
    my $dbh = DBI->connect('dbi:SQLite:dbname=:memory:', '', '', {
        RaiseError => 1,
    });
    $dbh->do('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');

    $dbh->prepare('INSERT INTO users (name) VALUES (?)')->execute('Alice');
    my $row = $dbh->selectrow_hashref('SELECT * FROM users WHERE name = ?', undef, 'Alice');
    is($row->{name}, 'Alice', 'Benutzer eingefügt und abgerufen');
};

done_testing;

Bewährte Vorgehensweisen

TUN

  • TDD befolgen: Tests vor der Implementierung schreiben (rot-grün-refaktorisieren)
  • Test2::V0 verwenden: Moderne Assertions, bessere Diagnostik
  • Subtests verwenden: Zusammengehörige Assertions gruppieren, Zustand isolieren
  • Externe Abhängigkeiten mocken: Netzwerk, Datenbank, Dateisystem
  • prove -l verwenden: Immer lib/ in @INC einschließen
  • Tests klar benennen: 'Benutzeranmeldung mit ungültigem Passwort schlägt fehl'
  • Randfälle testen: Leere Zeichenketten, undef, Null, Grenzwerte
  • 80%+ Abdeckung anstreben: Fokus auf Geschäftslogik-Pfade
  • Tests schnell halten: I/O mocken, In-Memory-Datenbanken verwenden

NICHT TUN

  • Nicht die Implementierung testen: Verhalten und Ausgabe testen, nicht Interna
  • Keinen Zustand zwischen Subtests teilen: Jeder Subtest sollte unabhängig sein
  • done_testing nicht auslassen: Stellt sicher, dass alle geplanten Tests gelaufen sind
  • Nicht übermäßig mocken: Nur Grenzen mocken, nicht den getesteten Code
  • Test::More nicht für neue Projekte verwenden: Test2::V0 bevorzugen
  • Testfehlschläge nicht ignorieren: Alle Tests müssen vor dem Merge bestanden werden
  • CPAN-Module nicht testen: Vertrauen Sie darauf, dass Bibliotheken korrekt funktionieren
  • Keine zerbrechlichen Tests schreiben: Übermäßig spezifischen Zeichenkettenabgleich vermeiden

Kurzreferenz

AufgabeBefehl / Muster
Alle Tests ausführenprove -lr t/
Einen Test ausführlich ausführenprove -lv t/unit/user.t
Parallelen Testlaufprove -lr -j8 t/
Abdeckungsberichtcover -test && cover -report html
Gleichheit testenis($got, $expected, 'label')
Tiefenvergleichis($got, hash { field k => 'v'; etc() }, 'label')
Ausnahme testenlike(dies { ... }, qr/msg/, 'label')
Keine Ausnahme testenok(lives { ... }, 'label')
Methode mockenTest::MockModule->new('Pkg')->mock(m => sub { ... })
Tests überspringenSKIP: { skip 'Grund', $anzahl unless $bedingung; ... }
TODO-TestsTODO: { local $TODO = 'Grund'; ... }

Häufige Fallstricke

done_testing vergessen

# Schlecht: Testdatei läuft, aber überprüft nicht, ob alle Tests ausgeführt wurden
use Test2::V0;
is(1, 1, 'funktioniert');
# Fehlendes done_testing – stille Fehler, wenn Testcode übersprungen wird

# Gut: Immer mit done_testing beenden
use Test2::V0;
is(1, 1, 'funktioniert');
done_testing;

Fehlendes -l-Flag

# Schlecht: Module in lib/ werden nicht gefunden
prove t/unit/user.t
# Can't locate MyApp/User.pm in @INC

# Gut: lib/ in @INC einschließen
prove -l t/unit/user.t

Übermäßiges Mocken

Mocken Sie die Abhängigkeit, nicht den getesteten Code. Wenn Ihr Test nur überprüft, dass ein Mock das zurückgibt, was Sie ihm gesagt haben, testet er nichts.

Testverschmutzung

Verwenden Sie my-Variablen innerhalb von Subtests – niemals our – um zu verhindern, dass Zustand zwischen Tests durchsickert.

Denken Sie daran: Tests sind Ihr Sicherheitsnetz. Halten Sie sie schnell, fokussiert und unabhängig. Verwenden Sie Test2::V0 für neue Projekte, prove zum Ausführen und Devel::Cover für die Rechenschaftspflicht.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills