Communitygithub.com

perl-testing

Padrões de teste em Perl usando Test2::V0, Test::More, executor prove, mocks, cobertura com Devel::Cover e metodologia TDD. Utilize ao escrever testes Perl com Test2::V0 ou Test::More, ou ao medir cobertura.

O que é perl-testing?

Este skill fornece um guia abrangente de padrões de teste para aplicações Perl, cobrindo desde os fundamentos com Test::More até o moderno framework Test2::V0, incluindo mocks, cobertura de código com Devel::Cover e a metodologia TDD (red-green-refactor). Ele detalha como usar assertions como `is`, `like`, `ok`, `dies` e `lives`, além de builders para comparação profunda de hashes e arrays. O skill também explica a organização de testes com o executor `prove`, configuração de fixtures, isolamento de subtests e práticas recomendadas para evitar armadilhas comuns, como esquecer `done_testing` ou poluir o estado entre testes. É útil para qualquer desenvolvedor que queira escrever testes robustos e manuteníveis em Perl, seja em projetos novos ou na migração de Test::More para Test2::V0. As plataformas suportadas incluem qualquer ambiente Perl com os módulos mencionados, e o conteúdo é baseado na documentação oficial e melhores práticas da comunidade Perl.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Padrões de Teste em Perl

Estratégias abrangentes de teste para aplicações Perl usando Test2::V0, Test::More, prove e metodologia TDD.

Quando Ativar

  • Ao escrever novo código Perl (siga TDD: red, green, refactor)
  • Ao projetar suites de teste para módulos ou aplicações Perl
  • Ao revisar a cobertura de testes Perl
  • Ao configurar infraestrutura de teste Perl
  • Ao migrar testes de Test::More para Test2::V0
  • Ao depurar testes Perl com falha

Fluxo de Trabalho TDD

Sempre siga o ciclo RED-GREEN-REFACTOR.

# Passo 1: RED — Escreva um teste com falha
# t/unit/calculator.t
use v5.36;
use Test2::V0;

use lib 'lib';
use Calculator;

subtest 'soma' => sub {
    my $calc = Calculator->new;
    is($calc->add(2, 3), 5, 'soma dois números');
    is($calc->add(-1, 1), 0, 'trata negativos');
};

done_testing;

# Passo 2: GREEN — Escreva a implementação mínima
# lib/Calculator.pm
package Calculator;
use v5.36;
use Moo;

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

1;

# Passo 3: REFACTOR — Melhore enquanto os testes permanecem verdes
# Execute: prove -lv t/unit/calculator.t

Fundamentos do Test::More

Módulo de teste Perl padrão — amplamente utilizado, incluído no core.

Assertions Básicas

use v5.36;
use Test::More;

# Planeje antecipadamente ou use done_testing
# plan tests => 5;  # Plano fixo (opcional)

# Igualdade
is($resultado, 42, 'retorna o valor correto');
isnt($resultado, 0, 'não é zero');

# Booleano
ok($usuario->is_active, 'usuário está ativo');
ok(!$usuario->is_banned, 'usuário não está banido');

# Comparação profunda
is_deeply(
    $obtido,
    { nome => 'Alice', papeis => ['admin'] },
    'retorna a estrutura esperada'
);

# Correspondência de padrão
like($erro, qr/não encontrado/i, 'erro menciona não encontrado');
unlike($saida, qr/senha/, 'saída oculta a senha');

# Verificação de tipo
isa_ok($obj, 'MinhaApp::Usuario');
can_ok($obj, 'salvar', 'excluir');

done_testing;

SKIP e TODO

use v5.36;
use Test::More;

# Pular testes condicionalmente
SKIP: {
    skip 'Nenhum banco de dados configurado', 2 a menos que $ENV{TEST_DB};

    my $db = connect_db();
    ok($db->ping, 'banco de dados está acessível');
    is($db->version, '15', 'versão correta do PostgreSQL');
}

# Marcar falhas esperadas
TODO: {
    local $TODO = 'Cache ainda não implementado';
    is($cache->get('chave'), 'valor', 'cache retorna valor');
}

done_testing;

Test2::V0 Framework Moderno

Test2::V0 é o substituto moderno para Test::More — assertions mais ricas, melhores diagnósticos e extensível.

Por que Test2?

  • Comparação profunda superior com construtores de hash/array
  • Melhor saída de diagnóstico em falhas
  • Subtests com escopo mais limpo
  • Extensível via plugins Test2::Tools::*
  • Compatível com versões anteriores de testes Test::More

Comparação Profunda com Construtores

use v5.36;
use Test2::V0;

# Construtor de hash — verifica estrutura parcial
is(
    $usuario->to_hash,
    hash {
        field nome  => 'Alice';
        field email => match(qr/\(a)example\.com$/);
        field idade   => validator(sub { $_ >= 18 });
        # Ignorar outros campos
        etc();
    },
    'usuário tem campos esperados'
);

# Construtor de array
is(
    $resultado,
    array {
        item 'primeiro';
        item match(qr/^segundo/);
        item DNE();  # Não Existe — verifica se não há itens extras
    },
    'resultado corresponde à lista esperada'
);

# Bag — comparação independente de ordem
is(
    $tags,
    bag {
        item 'perl';
        item 'teste';
        item 'tdd';
    },
    'tem todas as tags necessárias independente da ordem'
);

Subtests

use v5.36;
use Test2::V0;

subtest 'Criação de Usuário' => sub {
    my $usuario = Usuario->new(nome => 'Alice', email => '[email protected]');
    ok($usuario, 'objeto usuário criado');
    is($usuario->nome, 'Alice', 'nome está definido');
    is($usuario->email, '[email protected]', 'email está definido');
};

subtest 'Validação de Usuário' => sub {
    my $avisos = warns {
        Usuario->new(nome => '', email => 'ruim');
    };
    ok($avisos, 'avisa sobre dados inválidos');
};

done_testing;

Teste de Exceções com Test2

use v5.36;
use Test2::V0;

# Testa se o código morre
like(
    dies { dividir(10, 0) },
    qr/Divisão por zero/,
    'morre na divisão por zero'
);

# Testa se o código vive
ok(lives { dividir(10, 2) }, 'divisão bem-sucedida') or note($@);

# Padrão combinado
subtest 'tratamento de erros' => sub {
    ok(lives { parse_config('valido.json') }, 'config válida parseia');
    like(
        dies { parse_config('ausente.json') },
        qr/Não é possível abrir/,
        'arquivo ausente morre com mensagem'
    );
};

done_testing;

Organização de Testes e prove

Estrutura de Diretórios

t/
├── 00-carregar.t              # Verifica se os módulos compilam
├── 01-basico.t                # Funcionalidade principal
├── unit/
│   ├── config.t               # Testes unitários por módulo
│   ├── usuario.t
│   └── util.t
├── integration/
│   ├── database.t
│   └── api.t
├── lib/
│   └── TestHelper.pm          # Utilitários de teste compartilhados
└── fixtures/
    ├── config.json            # Arquivos de dados de teste
    └── usuarios.csv

Comandos prove

# Executar todos os testes
prove -l t/

# Saída verbosa
prove -lv t/

# Executar teste específico
prove -lv t/unit/usuario.t

# Busca recursiva
prove -lr t/

# Execução paralela (8 jobs)
prove -lr -j8 t/

# Executar apenas testes com falha da última execução
prove -l --state=failed t/

# Saída colorida com temporizador
prove -l --color --timer t/

# Saída TAP para CI
prove -l --formatter TAP::Formatter::JUnit t/ > resultados.xml

Configuração .proverc

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

Fixtures e Setup/Teardown

Isolamento de Subtest

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

subtest 'processamento de arquivo' => sub {
    # Setup
    my $dir = tempdir(CLEANUP => 1);
    my $arquivo = path($dir, 'entrada.txt');
    $arquivo->spew_utf8("linha1\nlinha2\nlinha3\n");

    # Teste
    my $resultado = process_arquivo("$arquivo");
    is($resultado->{conta_linhas}, 3, 'conta linhas');

    # Teardown acontece automaticamente (CLEANUP => 1)
};

Helpers de Teste Compartilhados

Coloque helpers reutilizáveis em t/lib/TestHelper.pm e carregue com use lib 't/lib'. Exporte funções de fábrica como create_test_db(), create_temp_dir() e fixture_path() via Exporter.

Mocks (Simulação)

Test::MockModule

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

subtest 'mock de API externa' => sub {
    my $mock = Test::MockModule->new('MinhaApp::API');

    # Bom: Mock retorna dados controlados
    $mock->mock(fetch_usuario => sub ($self, $id) {
        return { id => $id, nome => 'Usuário Mock', email => '[email protected]' };
    });

    my $api = MinhaApp::API->new;
    my $usuario = $api->fetch_usuario(42);
    is($usuario->{nome}, 'Usuário Mock', 'retorna usuário mockado');

    # Verificar contagem de chamadas
    my $contagem_chamadas = 0;
    $mock->mock(fetch_usuario => sub { $contagem_chamadas++; return {} });
    $api->fetch_usuario(1);
    $api->fetch_usuario(2);
    is($contagem_chamadas, 2, 'fetch_usuario chamado duas vezes');

    # Mock é automaticamente restaurado quando $mock sai de escopo
};

# Ruim: Monkey-patching sem restauração
# *MinhaApp::API::fetch_usuario = sub { ... };  # NUNCA — vaza entre testes

Para objetos mock leves, use Test::MockObject para criar dublês de teste injetáveis com ->mock() e verifique chamadas com ->called_ok().

Cobertura com Devel::Cover

Executando Cobertura

# Relatório básico de cobertura
cover -test

# Ou passo a passo
perl -MDevel::Cover -Ilib t/unit/usuario.t
cover

# Relatório HTML
cover -report html
open cover_db/coverage.html

# Limiares específicos
cover -test -report text | grep 'Total'

# Amigável para CI: falha abaixo do limiar
cover -test && cover -report text -select '^lib/' \
  | perl -ne 'if (/Total.*?(\d+\.\d+)/) { exit 1 if $1 < 80 }'

Teste de Integração

Use SQLite em memória para testes de banco de dados, mock HTTP::Tiny para testes de API.

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

subtest 'integração com banco de dados' => sub {
    my $dbh = DBI->connect('dbi:SQLite:dbname=:memory:', '', '', {
        RaiseError => 1,
    });
    $dbh->do('CREATE TABLE usuarios (id INTEGER PRIMARY KEY, nome TEXT)');

    $dbh->prepare('INSERT INTO usuarios (nome) VALUES (?)')->execute('Alice');
    my $linha = $dbh->selectrow_hashref('SELECT * FROM usuarios WHERE nome = ?', undef, 'Alice');
    is($linha->{nome}, 'Alice', 'usuário inserido e recuperado');
};

done_testing;

Melhores Práticas

FAÇA

  • Siga TDD: Escreva testes antes da implementação (red-green-refactor)
  • Use Test2::V0: Assertions modernas, melhores diagnósticos
  • Use subtests: Agrupe assertions relacionadas, isole estado
  • Mock dependências externas: Rede, banco de dados, sistema de arquivos
  • Use prove -l: Sempre inclua lib/ em @INC
  • Nomeie testes claramente: 'login de usuário com senha inválida falha'
  • Teste casos de borda: Strings vazias, undef, zero, valores limite
  • Busque 80%+ de cobertura: Foco em caminhos de lógica de negócio
  • Mantenha os testes rápidos: Mock I/O, use bancos em memória

NÃO FAÇA

  • Não teste implementação: Teste comportamento e saída, não detalhes internos
  • Não compartilhe estado entre subtests: Cada subtest deve ser independente
  • Não pule done_testing: Garante que todos os testes planejados foram executados
  • Não use mock em excesso: Simule apenas fronteiras, não o código sob teste
  • Não use Test::More em projetos novos: Prefira Test2::V0
  • Não ignore falhas de teste: Todos os testes devem passar antes do merge
  • Não teste módulos CPAN: Confie que as bibliotecas funcionam corretamente
  • Não escreva testes frágeis: Evite correspondência de string excessivamente específica

Referência Rápida

TarefaComando / Padrão
Executar todos os testesprove -lr t/
Executar um teste verbosoprove -lv t/unit/usuario.t
Execução em paraleloprove -lr -j8 t/
Relatório de coberturacover -test && cover -report html
Testar igualdadeis($obtido, $esperado, 'rótulo')
Comparação profundais($obtido, hash { field k => 'v'; etc() }, 'rótulo')
Testar exceçãolike(dies { ... }, qr/msg/, 'rótulo')
Testar sem exceçãook(lives { ... }, 'rótulo')
Simular um métodoTest::MockModule->new('Pkg')->mock(m => sub { ... })
Pular testesSKIP: { skip 'motivo', $contagem a menos que $cond; ... }
Testes TODOTODO: { local $TODO = 'motivo'; ... }

Armadilhas Comuns

Esquecer done_testing

# Ruim: O arquivo de teste executa mas não verifica se todos os testes foram executados
use Test2::V0;
is(1, 1, 'funciona');
# Faltando done_testing — bugs silenciosos se o código de teste for pulado

# Bom: Sempre termine com done_testing
use Test2::V0;
is(1, 1, 'funciona');
done_testing;

Flag -l Ausente

# Ruim: Módulos em lib/ não encontrados
prove t/unit/usuario.t
# Não é possível localizar MinhaApp/Usuario.pm em @INC

# Bom: Inclua lib/ em @INC
prove -l t/unit/usuario.t

Excesso de Mock

Simule a dependência, não o código sob teste. Se o seu teste apenas verifica que um mock retorna o que você disse a ele, ele não testa nada.

Poluição de Testes

Use variáveis my dentro de subtests — nunca our — para evitar vazamento de estado entre testes.

Lembre-se: Testes são sua rede de segurança. Mantenha-os rápidos, focados e independentes. Use Test2::V0 para novos projetos, prove para executar e Devel::Cover para responsabilidade.

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.

Habilidades Relacionadas