1---2name: php-testing-skill3description: Create, repair, and optimize automated tests in PHP projects that use PHPUnit, PestPHP, Laravel, Symfony, or custom app structures. Use when asked to identify the active PHP test stack, analyze `app/` and `config/`, add unit/feature/end-to-end tests, tune `phpunit.xml`, migrate or introduce Pest in PHPUnit projects, increase coverage safely, or create/update GitHub Actions for PHP test workflows.4---56# PHP Testing78## Quick Start910- Prefer the repository's current testing style, helpers, factories, fixtures, and naming before introducing new patterns.11- Detect the active stack first:12 - `composer.json` / `composer.lock` -- check PHPUnit and Pest major versions13 - `phpunit.xml` or `phpunit.xml.dist`14 - `tests/Pest.php`15 - `tests/`, `app/`, `src/`, `config/`, `.github/workflows/`16- Identify the PHPUnit major version early -- it determines annotation vs attribute syntax, available assertions, and mock API surface. See `references/framework-detection.md` for version-specific rules.17- Run `python3 ./scripts/survey_php_project.py <repo-root>` when the project is unfamiliar or large. Use the output to summarize frameworks, test tooling, app layout, configs, and CI.18- Read only the references needed for the current task:19 - `references/framework-detection.md`20 - `references/test-authoring.md`21 - `references/phpunit-optimization.md`22 - `references/github-actions.md`2324## Workflow25261. Detect the project type and test runner before writing code.272. Inventory the domain under `app/` or `src/`:28 - controllers, models/entities, services, repositories, jobs/commands, middleware, events/listeners, exceptions, policies, helpers, traits293. Inventory `config/`, `.env.example`, `phpunit.xml*`, and CI files to find:30 - database settings31 - queue/cache/session/mail drivers32 - parallel-test hooks33 - bootstrap and environment assumptions344. Match the closest existing test layer and style:35 - PHPUnit class style if the repo is PHPUnit-first36 - Pest syntax if the repo is Pest-first or mixed375. Create or repair the smallest test set that covers the requested behavior and the main regression path.386. Run the narrowest useful verification command first, then broaden if needed.397. If asked to optimize or modernize test tooling, adjust config after checking for project-specific constraints.4041## Test Layer Selection4243- Create **unit tests** for pure helpers, value objects, services with mocked boundaries, repositories with isolated query behavior, custom exceptions, resources, and small framework adapters.44- Create **feature/integration tests** for HTTP endpoints, console commands, jobs, notifications, policies, and database-backed flows.45- Treat **E2E** in PHP pragmatically:46 - If Pest v4 browser testing (`pestphp/pest-plugin-browser`), Laravel Dusk, Symfony Panther, Playwright, or another browser/E2E tool already exists, extend that stack.47 - If no browser stack exists, implement end-to-end coverage as full application flow tests through HTTP, console, queues, or persistence boundaries using the repo's existing framework test harness.48 - Pest v4 browser tests use Playwright under the hood and support device simulation, dark mode, smoke testing (`assertNoSmoke()`), and visual regression (`assertScreenshotMatches()`). Prefer this over Dusk for new Pest v4 projects unless Dusk is already established.49- Do not add a new browser framework unless the user asks for it or the repo already supports it.5051## App And Config Analysis5253- Start with directory shape, not assumptions. Top-level folders often reveal the architecture faster than individual classes.54- In `app/` or `src/`, identify the project's real seams:55 - service layer and orchestration points56 - repositories or query objects57 - model/entity relationships58 - exception hierarchies59 - HTTP/controller validation boundaries60 - async boundaries such as jobs, events, listeners, mail, notifications61- In `config/`, focus on settings that change test behavior:62 - database connections63 - cache/queue/session/mail drivers64 - feature flags65 - third-party service toggles66 - auth and guard configuration67 - filesystem disks68- Prefer describing the discovered conventions back to the user through the resulting tests and config changes instead of writing generic boilerplate.6970## Working In Mixed PHPUnit And Pest Repos7172- Expect mixed repos. A project can depend on both `phpunit/phpunit` and `pestphp/pest`.73- If Pest is installed and `tests/Pest.php` exists, prefer Pest for new tests unless nearby files strongly suggest PHPUnit classes.74- If the repo is PHPUnit-only and the user asks for Pest:75 - install Pest packages consistent with the framework76 - create `tests/Pest.php`77 - keep existing PHPUnit tests working78 - convert incrementally, not as a large rewrite, unless explicitly requested79 - consider `pestphp/pest-plugin-drift` for automated conversion of PHPUnit classes to Pest syntax80- Never convert an existing suite wholesale just to satisfy a small feature request.8182## PHPUnit Version Awareness8384- PHPUnit 12+ **removed all docblock annotations** (`@covers`, `@dataProvider`, `@test`, etc.). Use PHP 8 attributes exclusively.85- PHPUnit 10-11 support both annotations and attributes. Prefer attributes for new code but match existing convention.86- PHPUnit 10 removed `withConsecutive()`. PHPUnit 13 provides `withParameterSetsInOrder()` and `withParameterSetsInAnyOrder()` as replacements. For PHPUnit 10-12, use chained `willReturnOnConsecutiveCalls()` or manual callback logic.87- PHPUnit 12+ removed `getMockForAbstractClass()`, `getMockForTrait()`, and `createTestProxy()`. Use `createMock()` or `createStub()` instead.88- PHPUnit 13 introduces sealed mock objects (`seal()`) and eight new array assertion methods.89- Always check the project's PHPUnit major version before writing test metadata or mock code. Use `--migrate-configuration` to update `phpunit.xml` schema when upgrading.9091## Regression Safety9293- When fixing a bug, reproduce the failing behavior first in a focused test.94- When adjusting existing tests, preserve helper contracts, dataset shape, fixture behavior, and shared bootstrap conventions.95- Prefer surgical assertions over broad snapshots or brittle string matching.96- When mocks are necessary, mock external boundaries rather than the class under test.97- If the requested change risks altering global test config, add the minimal config needed and explain the tradeoff.9899## Verification100101- Prefer narrow commands first, for example:102 - `./vendor/bin/pest tests/Unit/Services/FooTest.php`103 - `./vendor/bin/pest --filter="it_handles_foo"`104 - `./vendor/bin/phpunit tests/Feature/BarTest.php`105 - `php artisan test --filter=FooTest`106- Broaden after the target passes:107 - `./vendor/bin/pest --testsuite=Unit`108 - `./vendor/bin/pest --testsuite=Feature`109 - `composer test`110- If verification cannot run because dependencies, services, or local infra are unavailable, state that clearly and describe the expected command.111112## References113114- `references/framework-detection.md`: Detect PHPUnit, Pest, Laravel, Symfony, and mixed setups.115- `references/test-authoring.md`: Patterns for unit, feature, and pragmatic E2E tests plus repair workflow.116- `references/phpunit-optimization.md`: Fast `phpunit.xml` and execution strategies.117- `references/github-actions.md`: CI templates and optimization guidance for Laravel, Symfony, and plain PHP.