name: test-writing
description: SDLC Phase: TESTING. Comprehensive code verification, efficient test execution, and failing test diagnosis — prioritizes lightweight verification over full test suite to conserve memory and time.
upstream:
- feature-building
- code-refactoring
- livewire-development
- medialibrary-development
downstream:
- sync-docs
- pest-testing
Verify & Testing
Prerequisite: Load context-awareness for project conventions and critical invariants.
When to Activate
Use this skill when:
- Verifying code changes before committing (any layer)
- Deciding what verification strategy to use for a given change
- Writing new tests or fixing failing tests
- Determining whether running the full test suite is necessary
Core Principle: Verify First, Test Second
The full test suite consumes ~2GB+ RAM and takes 10+ minutes. Always ask: can I verify this change
without running tests?
1. Verification Strategy by Change Type
| Change Type | What to Run | Why Not Full Suite |
|---|
Translation keys (lang/*.php) | php -l each file, then php artisan tinker --execute="echo __('key');" for both en and id | No logic change; full suite won't catch missing keys |
Config files (config/*.php) | php artisan config:cache (dry run) or visual inspection | Config is loaded at boot; full suite irrelevant |
| Docs / Markdown | Visual inspection only | Zero runtime impact |
| Blade templates | npm run build (if using Vite) | Frontend only; no PHP test needed |
| CSS / JS / NPM | npm run build, check for errors | Purely frontend |
| Helper / utility function | Quick tinker test: php artisan tinker --execute="dump(myFunction('test'));" | Validate contract before writing test |
| Single method refactor | Targeted: php artisan test --compact --filter={ClassName} | Isolated change, test only the affected class |
| Cross-module refactor | vendor/bin/pest --testsuite={ModuleName} (run affected module suites) | Integration risk — module integration tests cover real DB |
| New Action / Model / Service | Full suite ONCE after all changes batched | Highest risk — verify nothing broke elsewhere |
| Composer dependency bump | Run affected module suites: vendor/bin/pest --testsuite={ModuleName} | Lock-only changes rarely break unit tests |
| NPM dependency bump | npm run build | Frontend only |
Lightweight Verification Toolkit
php -l path/to/file.php
php artisan tinker --execute="echo __('my.key', [], 'en'); echo PHP_EOL; echo __('my.key', [], 'id');"
php artisan tinker --execute="new MyClass();"
php artisan system:health
php artisan config:cache
npm run build
2. Efficient Test Execution
Targeted Test Commands
php artisan test --compact --filter={ClassName}
php artisan test --compact --filter="ActionResponse|BaseFormRequest|LangChecker" \
&& php artisan test --compact --filter="CertificateStatus"
vendor/bin/pest --testsuite={ModuleName}
php artisan test --compact
Batch Execution Rule
NEVER run tests after every individual change. Follow this order:
- Make ALL planned changes to ALL files
- Run
php -l on every changed PHP file (quick syntax check)
- Verify logic with tinker or artisan commands if possible
- Run targeted test(s) — only the affected test class(es)
- Only if changes affect core infrastructure → run full suite
Test Memory Considerations
- Full suite: ~2GB RAM, 10-15 minutes
- Feature suite: ~1.5GB RAM, 8-12 minutes
- Unit suite: ~500MB RAM, 2-4 minutes
- Single test class: ~200MB RAM, 5-60 seconds
Choose the smallest scope that gives confidence.
3. Writing Tests Efficiently
Follow Existing Patterns (Don't Reinvent)
Before writing a test, always read an existing test file of the same type:
- For a Command Action test → read another Command Action test
- For a Livewire component test → read another Livewire test
- For an Entity test → read another Entity test
Copy the structure, imports, and patterns. This avoids:
- Wrong base class usage
- Missing
LazilyRefreshDatabase or use RefreshDatabase decisions
- Inconsistent assertion style
Minimal Test Coverage Checklist
Not every test needs 100% coverage on the first pass. Cover:
Skip until explicitly needed:
- HTTP status code assertions (for API-only changes)
- UI rendering details (CSS classes, DOM structure) — unless the task requires it
Test Helper Pattern
When test setup is repetitive (e.g., creating the same model hierarchy), extract into a helper:
function createEnrolledStudent(): User
{
$school = School::factory()->create();
$department = Department::factory()->for($school)->create();
$student = User::factory()->student()->create();
$student->departments()->attach($department);
return $student;
}
4. Fixing Failing Tests
Diagnosis Protocol
When a test fails, follow this order to minimize wasted runs:
-
Read the failure message — don't re-run blindly
Failed asserting that... → assertion mismatch
Class "X" not found → autoload/namespace issue
Call to undefined method → wrong import or missing method
SQLSTATE[HY000] → database/migration issue
-
Read the exact line — open the test file and understand the assertion
-
Determine root cause — is it the test, the source code, or the environment?
- Test failing on
main too? → pre-existing issue, not your change
- Test passes in isolation but fails in suite? → shared state / ordering issue
- Test fails only on CI? → environment-specific (DB driver, extension)
Efficient Fix Workflow
php artisan test --compact --filter={FailingTest}
php artisan test --compact --filter={FailingTest}
php artisan test --compact --filter={ClassName}
Common Test Failures & Fixes
| Symptom | Likely Cause | Fix |
|---|
Class "X" not found | Wrong namespace in use or autoload stale | Run composer dump-autoload |
Failed asserting that... | Logic mismatch | Check source logic vs test expectation |
Call to undefined method | Wrong mock or missing import | Check use statements |
SQLSTATE[HY000]: General error | Migration missing / DB not fresh | php artisan migrate:fresh --seed |
The :attribute field is required | Missing test data | Check factory / DTO defaults |
| Session store not set | Livewire test missing request setup | Use actingAs() with Livewire |
header may not contain... | Response content type mismatch | Add ->assertJson() or explicit header |
| Test times out | Infinite loop / queue not processed | Add Queue::fake() or Bus::fake() |
Pre-existing Failure Handling
If a test was already failing before your changes:
- Flag it to the user
- Do NOT attempt to fix it unless the user asks
- Document it as a known pre-existing issue
- Verify your changes didn't introduce NEW failures
5. Full Suite Fire Drill
Only run the full suite when:
- Before pushing a branch that modifies
app/ logic
- After upgrading any Composer or NPM dependency
- After refactoring core infrastructure (Base classes, Traits, Contracts)
php artisan test --compact \
&& vendor/bin/pint --dirty --format agent \
&& vendor/bin/phpstan analyse --no-progress
During full suite run:
- Do NOT interrupt — let it finish
- If it fails, note the failing test(s) and diagnose after completion
- Do NOT fix and re-run the full suite — just run the failing test class
Phase Context
| Role | Skill |
|---|
| Upstream | feature-building (code to verify), code-refactoring (refactored code) |
| This skill | VERIFY & TEST — verification strategy, efficient test execution, fix diagnosis |
| Downstream | pest-testing (detailed test writing), sync-docs (doc updates after verification) |
Automation Scripts
| Script | What it does | Command |
|---|
scan_tests.py | Run test suite, parse per-module results | python3 scripts/scan_tests.py |
Use --module {Name} to scope. Output: scripts/outputs/{timestamp}-tests.json.
Quality Gate — arch-guard
Test files must also pass arch-guard checks:
- Test files must have
declare(strict_types=1) (D1)
- No debug calls in tests (D2) — use
->dd() or ->dump() Pest methods instead
- Test naming follows
it_{behavior}() convention
- See
arch-guard skill for full rule reference
References
| Topic | Location |
|---|
| Testing patterns | docs/architecture/testing-pattern.md |
| Pest testing skill | .agents/skills/pest-testing/SKILL.md |
| arch-guard skill | .agents/skills/arch-guard/SKILL.md |
| Pre-commit checklist | AGENTS.md (end of file) |
| Critical invariants | AGENTS.md (§Critical Invariants) |