Agent skill
testing
Cria testes com Pest PHP incluindo Feature, Unit, HTTP e Datasets. Use quando precisar escrever testes, criar test suites, ou implementar TDD em projetos Laravel com Pest.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/testing-aronpc-ai
Metadata
Additional technical details for this skill
- author
- aronpc
- version
- 1.0.0
- category
- laravel
SKILL.md
testing
Resumo
Cria e mantém testes com Pest PHP para Feature, Unit, HTTP, Actions e Policies.
Skills Relacionadas
| Skill | Quando usar junto |
|---|---|
qa |
Para validação completa de qualidade |
coder |
Para criar testes durante implementação |
architecture |
Para testar Actions e Policies |
models |
Para criar Factories e testar Models |
cicd |
Para pipelines de teste |
pr-review |
Para verificar cobertura em PRs |
Quando usar
Use esta skill sempre que:
- Escrever testes para novas features
- Aumentar cobertura de testes
- Refatorar testes existentes
- Criar mocks customizados
- Testar Actions, Policies, Models
- Testar endpoints HTTP
- Criar datasets para testes
- Configurar testes de multi-tenancy
Instalação e Configuração
Instalar Pest
composer require pestphp/pest --dev --with-all-dependencies
php artisan pest:install
Estrutura de Testes
tests/
├── Feature/ # Testes de funcionalidade
│ ├── Http/
│ │ ├── Controllers/
│ │ └── Resources/
│ ├── Actions/
│ ├── Policies/
│ └── Jobs/
├── Unit/ # Testes unitários
│ ├── Models/
│ ├── Enums/
│ └── Services/
└── Pest.php # Configuração Pest
Criar Teste
# Feature test
php artisan make:test ProductTest --pest
# Unit test
php artisan make:test Models/ProductTest --pest --unit
Testes de HTTP
Teste Básico de Endpoint
<?php
use function Pest\Laravel\{actingAs, get};
use App\Models\User;
use App\Models\Product;
it('can view products list', function () {
$user = User::factory()->create();
Product::factory()->count(3)->create();
actingAs($user)
->get(route('products.index'))
->assertOk()
->assertSee(__('messages.resources.products'))
->assertViewHas('products');
});
it('cannot view products without auth', function () {
get(route('products.index'))
->assertRedirect(route('login'));
});
Teste com Dados
it('can create a product', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
actingAs($user)
->post(route('products.store'), [
'name' => 'Test Product',
'price' => 100.00,
'category_id' => $category->id,
])
->assertRedirect(route('products.index'))
->assertSessionHas('success');
$this->assertDatabaseHas('products', [
'name' => 'Test Product',
'price' => 10000, // em centavos
'category_id' => $category->id,
]);
});
Testes de Actions
<?php
use App\Actions\Product\CreateProductAction;
use App\DataObjects\Product\CreateProductData;
use App\Models\Tenant;
use function Pest\Laravel\{assertDatabaseHas};
it('creates a product successfully', function () {
// Arrange
$tenant = Tenant::factory()->create();
$data = CreateProductData::fromRequest([
'name' => 'Test Product',
'price' => 100.00,
'category_id' => Category::factory()->create()->id,
]);
// Act
$product = CreateProductAction::run($tenant, $data);
// Assert
expect($product->name)->toBe('Test Product');
assertDatabaseHas('products', [
'id' => $product->id,
'tenant_id' => $tenant->id,
]);
});
it('throws exception when product limit exceeded', function () {
$tenant = Tenant::factory()->create();
$tenant->plan->update(['limits' => ['products' => 1]]);
Product::factory()->create(['tenant_id' => $tenant->id]);
$data = CreateProductData::fromRequest([
'name' => 'Second Product',
'price' => 100.00,
]);
expect(fn() => CreateProductAction::run($tenant, $data))
->toThrow(ProductLimitExceededException::class);
});
Testes de Policies
<?php
use App\Models\Product;
use App\Models\User;
use App\Policies\ProductPolicy;
it('allows owner to view product', function () {
$user = User::factory()->create();
$product = Product::factory()->create(['tenant_id' => $user->tenant_id]);
expect((new ProductPolicy)->view($user, $product))->toBeTrue();
});
it('denies non-owner to view product', function () {
$user = User::factory()->create();
$otherTenant = Tenant::factory()->create();
$product = Product::factory()->create(['tenant_id' => $otherTenant->id]);
expect((new ProductPolicy)->view($user, $product))->toBeFalse();
});
it('allows creation when within limit', function () {
$user = User::factory()->create();
$user->tenant->plan->update(['limits' => ['products' => 10]]);
expect((new ProductPolicy)->create($user))->toBeTrue();
});
Testes de Models
<?php
use App\Models\Product;
it('has a category', function () {
$product = Product::factory()
->for(Category::factory())
->create();
expect($product->category)->toBeInstanceOf(Category::class);
});
it('can check if is active', function () {
$activeProduct = Product::factory()->active()->create();
$inactiveProduct = Product::factory()->inactive()->create();
expect($activeProduct->isActive())->toBeTrue();
expect($inactiveProduct->isActive())->toBeFalse();
});
it('uses correct table name', function () {
expect((new Product())->getTable())->toBe('products');
});
Datasets
Criar Dataset
<?php
use App\Enums\ProductStatus;
dataset('product_statuses', [
'active' => [ProductStatus::ACTIVE, true],
'inactive' => [ProductStatus::INACTIVE, false],
'draft' => [ProductStatus::DRAFT, false],
]);
it('correctly determines if product is viewable', function ($status, $expected) {
$product = Product::factory()->create(['status' => $status]);
expect($product->isViewable())->toBe($expected);
})->with('product_statuses');
Dataset com Objetos
dataset('users', [
'admin' => fn() => User::factory()->admin()->create(),
'regular' => fn() => User::factory()->create(),
'guest' => null,
]);
it('can access dashboard based on role', function ($user) {
if ($user) {
actingAs($user)
->get('/dashboard')
->assertOk();
} else {
get('/dashboard')
->assertRedirect('/login');
}
})->with('users');
Mocking
Mock Events
<?php
use App\Events\ProductCreated;
use Illuminate\Support\Facades\Event;
it('dispatches product created event', function () {
Event::fake([ProductCreated::class]);
CreateProductAction::run($tenant, $data);
Event::assertDispatched(ProductCreated::class, function ($event) use ($product) {
return $event->product->id === $product->id;
});
});
it('does not dispatch event on validation error', function () {
Event::fake([ProductCreated::class]);
// Tentativa inválida
// ...
Event::assertNotDispatched(ProductCreated::class);
});
Mock Mail
<?php
use Illuminate\Support\Facades\Mail;
use App\Mail\ProductCreatedMail;
it('sends email when product is created', function () {
Mail::fake();
CreateProductAction::run($tenant, $data);
Mail::assertSent(ProductCreatedMail::class, function ($mail) use ($product) {
return $mail->product->id === $product->id;
});
});
Mock Queue
<?php
use Illuminate\Support\Facades\Queue;
use App\Jobs\ProcessProductImage;
it('queues image processing job', function () {
Queue::fake();
CreateProductAction::run($tenant, $data);
Queue::assertPushed(ProcessProductImage::class, function ($job) use ($product) {
return $job->product->id === $product->id;
});
});
Mock Storage
<?php
use Illuminate\Support\Facades\Storage;
it('uploads product image', function () {
Storage::fake('s3');
$file = UploadedFile::fake()->image('product.jpg');
// Upload
$path = $file->store('products', 's3');
Storage::disk('s3')->assertExists($path);
});
Mock Notifications
<?php
use Illuminate\Support\Facades\Notification;
use App\Notifications\ProductCreatedNotification;
it('sends notification when product is created', function () {
Notification::fake();
CreateProductAction::run($tenant, $data);
Notification::assertSentTo(
$tenant->owner,
ProductCreatedNotification::class
);
});
Helpers Pest/Laravel
actingAs
actingAs($user)
->get('/dashboard')
->assertOk();
get, post, put, delete, patch
get('/products')
->assertOk();
post('/products', $data)
->assertRedirect();
put('/products/1', $data)
->assertRedirect();
delete('/products/1')
->assertRedirect();
assertDatabaseHas, assertDatabaseMissing
assertDatabaseHas('products', [
'name' => 'Test Product',
]);
assertDatabaseMissing('products', [
'name' => 'Deleted Product',
]);
assertSoftDeleted
assertSoftDeleted('products', [
'id' => $productId,
]);
Testes de Multi-tenancy
<?php
it('tenant can only see their products', function () {
$tenant1 = Tenant::factory()->create();
$tenant2 = Tenant::factory()->create();
Product::factory()->count(3)->create(['tenant_id' => $tenant1->id]);
Product::factory()->count(5)->create(['tenant_id' => $tenant2->id]);
actingAs($tenant1->owner)
->get(route('products.index'))
->assertViewHas('products', function ($products) use ($tenant1) {
return $products->count() === 3
&& $products->every->tenant_id === $tenant1->id;
});
});
it('cannot access another tenant product', function () {
$tenant1 = Tenant::factory()->create();
$tenant2 = Tenant::factory()->create();
$product = Product::factory()->create(['tenant_id' => $tenant2->id]);
actingAs($tenant1->owner)
->get(route('products.show', $product))
->assertForbidden();
});
Testes de Jobs
<?php
use App\Jobs\SyncProductInventory;
use function Pest\Laravel\{assertDatabaseHas};
it('syncs product inventory', function () {
$product = Product::factory()->create();
SyncProductInventory::dispatchSync($product);
assertDatabaseHas('products', [
'id' => $product->id,
'inventory_synced_at' => now(),
]);
});
Plugins Recomendados
Pest Parallel
composer require pestphp/pest-plugin-parallel --dev
// tests/Pest.php
use Pest\Parallel\Paratest;
Paratest::process();
Coverage
php artisan pest --coverage
Melhores Práticas
✅ FAÇA
- Use testes descritivos com
it()etest() - Siga padrão Arrange-Act-Assert
- Use factories para criar dados de teste
- Use datasets para testar múltiplos cenários
- Mock externos (Mail, Queue, Storage, Events)
- Teste exceções com
toThrow() - Use helpers Pest/Laravel para brevidade
- Teste authorization em Policies
- Teste validação em Form Requests
- Teste multi-tenancy separadamente
- Mantenha testes rápidos e isolados
- Use transações para rollback automático
❌ NÃO FAÇA
- Não teste código de framework
- Não use lógica complexa em testes
- Não deixe testes lentos sem otimização
- Não esqueça de limpar dados após testes
- Não teste múltiplas coisas em um teste
- Não dependa de ordem de testes
- Não use produção database para testes
- Não esqueça de mock serviços externos
Checklist de Testes
Antes de finalizar QUALQUER feature:
- Testes de HTTP endpoints criados
- Testes de Actions criados
- Testes de Policies criados
- Testes de Models criados (se necessário)
- Mocks configurados (Mail, Queue, Events)
- Datasets para cenários múltiplos
- Testes de multi-tenancy (se aplicável)
- Cobertura de código verificada
- Todos testes passando
Cobertura de Código
# Gerar relatório de cobertura
php artisan pest --coverage --min=80
# Gerar HTML coverage
php artisan pest --coverage --coverage-html=coverage
Referências Cruzadas
- Actions: Teste Actions criadas com
laravel-actions-events - Policies: Teste Policies definidas em
laravel-architecture - Exceptions: Teste exceções de
laravel-exceptions - Filament: Teste Resources de
laravel-filament - i18n: Teste traduções de
laravel-i18n
Referências
- Pest Documentation - Documentação oficial
- Laravel Testing - Documentação Laravel
- Pest Plugins - Plugins disponíveis
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?