testing-knowledge
Testing knowledge base for PHP 8.4 projects. Provides testing pyramid, AAA pattern, naming conventions, isolation principles, DDD testing guidelines, and PHPUnit patterns.
Works with
---
name: testing-knowledge
description: Testing knowledge base for PHP 8.4 projects. Provides testing pyramid, AAA pattern, naming conventions, isolation principles, DDD testing guidelines, and PHPUnit patterns.
license: MIT
---
# Testing Knowledge Base
Quick reference for PHP testing patterns, principles, and best practices.
## Testing Pyramid
```
/\
/ \ Functional (10%)
/────\ - E2E, browser tests
/ \ - Slow, fragile
/────────\ Integration (20%)
/ \ - DB, HTTP, queues
/────────────\Unit (70%)
/ \- Fast, isolated
/________________\- Business logic
```
**Rule:** 70% unit, 20% integration, 10% functional. Invert the pyramid = slow, brittle test suite.
## AAA Pattern (Arrange-Act-Assert)
```php
public function test_order_calculates_total_with_discount(): void
{
// Arrange — set up test data
$order = new Order(OrderId::generate());
$order->addItem(new Product('Book', Money::EUR(100)));
$discount = new PercentageDiscount(10);
// Act — execute the behavior
$total = $order->calculateTotal($discount);
// Assert — verify the outcome
self::assertEquals(Money::EUR(90), $total);
}
```
**Rules:**
- One blank line between sections
- Single Act per test
- Assert behavior, not implementation
## Naming Conventions
### PHPUnit Style
```
test_{method}_{scenario}_{expected}
```
| Example | Method | Scenario | Expected |
|---------|--------|----------|----------|
| `test_calculate_total_with_discount_returns_reduced_amount` | calculateTotal | with discount | returns reduced amount |
| `test_confirm_when_already_shipped_throws_exception` | confirm | when already shipped | throws exception |
| `test_email_with_invalid_format_fails_validation` | Email (VO) | with invalid format | fails validation |
### Pest Style
```php
it('calculates total with discount applied')
it('throws exception when confirming shipped order')
it('fails validation for invalid email format')
```
## Test Isolation Principles
### DO
- [ ] Fresh fixtures per test
- [ ] Independent test execution (any order)
- [ ] Teardown cleans all state
- [ ] Use in-memory implementations
### DON'T
- [ ] Shared mutable state between tests
- [ ] Tests depending on execution order
- [ ] Global variables or singletons
- [ ] Real external services in unit tests
## Quick Quality Checklist
| Rule | Check |
|------|-------|
| One test = one behavior | Single assertion group |
| Test is documentation | Name reads as specification |
| No logic in tests | No if/for/while |
| Fast execution | <100ms per unit test |
| Mock interfaces only | Never mock VO, Entity, final |
| ≤3 mocks per test | More = design smell |
| Behavior over implementation | Test WHAT, not HOW |
## DDD Component Testing
| Component | Test Focus | Mocks Allowed |
|-----------|------------|---------------|
| **Value Object** | Validation, equality, immutability | None |
| **Entity** | State transitions, business rules | None |
| **Aggregate** | Invariants, consistency, events | None |
| **Domain Service** | Business logic spanning aggregates | Repository (Fake) |
| **Application Service** | Orchestration, transactions | Repository, EventDispatcher |
| **Repository** | CRUD operations | Database (SQLite) |
## PHP 8.4 Test Patterns
### Unit Test Template
```php
<?php
declare(strict_types=1);
namespace Tests\Unit\Domain;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
#[Group('unit')]
#[CoversClass(Email::class)]
final class EmailTest extends TestCase
{
public function test_creates_valid_email(): void
{
$email = new Email('user@example.com');
self::assertSame('user@example.com', $email->value);
}
public function test_throws_for_invalid_format(): void
{
$this->expectException(InvalidArgumentException::class);
new Email('invalid');
}
}
```
### Integration Test Template
```php
<?php
declare(strict_types=1);
namespace Tests\Integration\Infrastructure;
use PHPUnit\Framework\Attributes\Group;
use Tests\DatabaseTestCase;
#[Group('integration')]
final class DoctrineOrderRepositoryTest extends DatabaseTestCase
{
private OrderRepositoryInterface $repository;
protected function setUp(): void
{
parent::setUp();
$this->repository = $this->getContainer()->get(OrderRepositoryInterface::class);
}
public function test_saves_and_retrieves_order(): void
{
// Arrange
$order = OrderMother::pending();
// Act
$this->repository->save($order);
$found = $this->repository->findById($order->id());
// Assert
self::assertNotNull($found);
self::assertTrue($order->id()->equals($found->id()));
}
}
```
## Test Doubles Quick Reference
| Type | Purpose | When to Use |
|------|---------|-------------|
| **Stub** | Returns canned answers | External API responses |
| **Mock** | Verifies interactions | Event publishing |
| **Fake** | Working implementation | InMemory repository |
| **Spy** | Records calls | Logging, notifications |
### Decision Matrix
```
Need to verify a call was made?
├── Yes → Mock or Spy
└── No → Need real behavior?
├── Yes → Fake
└── No → Stub
```
## Common Test Smells
| Smell | Detection | Fix |
|-------|-----------|-----|
| Logic in Test | `if`, `for`, `while` in test | Extract to helper or parameterize |
| Mock Overuse | >3 mocks | Refactor design, use Fakes |
| Mystery Guest | External files, hidden data | Inline test data or use Builder |
| Eager Test | Tests multiple behaviors | Split into separate tests |
| Fragile Test | Breaks on refactor | Test behavior, not implementation |
## Advanced Testing Patterns
### Contract Testing (Pact)
| Aspect | Unit Test | Integration Test | Contract Test |
|--------|-----------|------------------|---------------|
| Speed | Fast | Slow | Medium |
| Scope | Single class | Service + deps | API boundary |
| Isolation | Full | Partial | Consumer/Provider |
| Use case | Business logic | DB, queues | Service-to-service |
**When to use:** Microservices REST APIs, message-based systems, event schema verification.
### Load Testing Patterns
| Pattern | Duration | Load Profile | Goal |
|---------|----------|--------------|------|
| Smoke | 1-2 min | Minimal | Verify script works |
| Load | 10-30 min | Expected traffic | Performance baseline |
| Stress | 10-30 min | 1.5-2x expected | Find breaking point |
| Spike | 5-10 min | Sudden burst | Test auto-scaling |
| Soak | 2-8 hours | Sustained | Find memory leaks |
### Chaos Testing (Failure Injection)
| Failure | How to Inject | What It Tests |
|---------|---------------|---------------|
| Network latency | Sleep in middleware | Timeout handling |
| Service error | Return 500 randomly | Circuit breaker |
| Connection refused | Close port | Fallback behavior |
| Slow database | Query delay | Query timeout handling |
### E2E Distributed Testing
| Strategy | How | Trade-off |
|----------|-----|-----------|
| Test containers | Docker Compose per test | Isolated but slow |
| Shared staging | Dedicated environment | Fast but interference |
| Data seeding | API/DB setup per test | Controlled but complex |
| Snapshot restore | DB snapshot before tests | Fast reset |
## References
For detailed information, load these reference files:
- `references/unit-testing.md` — Unit test patterns and examples
- `references/integration-testing.md` — Integration test setup and patterns
- `references/ddd-testing.md` — Testing DDD components (VO, Entity, Aggregate, Service)
- `references/advanced-testing.md` — Contract testing (Pact), chaos testing, load testing patterns (ramp-up, spike, soak), E2E distributed testingMore Testing skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
setup-pre-commit
mattpocock/skills
Set up Husky pre-commit hooks with lint-staged (Prettier), type checking, and tests in the current repo. Use when user wants to add pre-commit hooks, set up Husky, configure lint-staged, or add commit-time formatting/typechecking/testing.
agent-browser
vercel-labs/agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.

