Laravel Tester
Instructions
Models Testing Policy
- DO NOT create unit tests for Laravel Eloquent models.
- Rationale:
- Laravel's Eloquent ORM is extensively tested by the Laravel team
- Testing basic CRUD operations, relationships, and standard functionality
provides no value
- Models are excluded from code coverage metrics (see phpunit.xml)
- What NOT to test:
- Basic relationships (hasOne, hasMany, belongsTo, etc.)
- Simple CRUD operations (create, update, delete, find)
- Standard Eloquent functionality
- Factory creation without custom logic
- Basic fillable/guarded attributes
- Standard casting functionality
- Exceptions — What TO test:
- Custom business logic methods
- Complex accessors/mutators with business rules
- Custom scopes with specific logic
- Observer behavior and side effects
- Mass assignment protection (if critical)
- Where to test model functionality instead:
- Feature tests via HTTP endpoints and workflows
- Integration tests for model interactions
- Observer tests for event handlers
- Action/Service tests for business logic
Framework
- Pest PHP - Modern testing framework with BDD-style syntax
- Mutation Testing with Infection for test quality assurance
- Architectural Testing to enforce code structure rules
Test Structure
tests/
├── Feature/ # Integration tests
│ ├── Auth/
│ ├── MentorPrograms/
│ └── Pages/
├── Unit/ # Unit tests
│ ├── Actions/
│ ├── Models/
│ ├── Observers/
│ └── Support/
├── Pest.php # Pest configuration
└── TestCase.php # Base test case
Running Tests
All tests must be run in a Docker container. Feature tests do not need to
mutate.
With Docker
# Run all tests
docker compose exec app ./vendor/bin/pest
# Run with coverage
docker compose exec app ./vendor/bin/pest --coverage
# Run mutation testing
docker compose exec app ./vendor/bin/pest --mutate --covered-only --parallel --min=100
# Run specific test file
docker compose exec app ./vendor/bin/pest tests/Unit/ExampleTest.php
Local Environment
# Ensure test database is configured in .env.testing
./vendor/bin/pest
./vendor/bin/pest --coverage
./vendor/bin/pest --mutate --covered-only --parallel --min=100
Test Configuration
- Database: Uses RefreshDatabase trait for clean test state
- Environment: Configured in phpunit.xml with testing-specific settings
- Coverage: Reports generated in
coverage/ directory
- Memory Limit: 512M for test execution
Writing Tests
Basic Test Structure
<?php
declare(strict_types=1);
// For mutation testing (optional)
mutates(YourClass::class);
describe('Feature Description', function (): void {
beforeEach(function (): void {
// Setup code
$this->user = User::factory()->create();
});
it('describes what it tests', function (): void {
$result = someFunction();
expect($result)->toBe('expected_value');
});
});
Testing Actions (Laravel Actions Pattern)
it('handles the action correctly', function (): void {
$action = new YourAction();
$result = $action->handle($request, $parameters);
expect($result)
->toBeInstanceOf(RedirectResponse::class)
->and($result->getTargetUrl())
->toBe(route('expected.route'));
});
Architectural Testing
The project enforces architectural rules via tests/Unit/ArchTest.php:
- No debugging functions in production code
- Models must extend Eloquent Model
- Page actions must have 'Page' suffix
- Enums must be proper enum classes
Common Commands
# Code quality checks
./vendor/bin/phpstan analyse
./vendor/bin/pint --test
./vendor/bin/rector process --dry-run
# Fix code issues
./vendor/bin/pint
./vendor/bin/rector process
# Generate IDE helpers
php artisan ide-helper:generate
php artisan ide-helper:models
# Clear caches
php artisan config:clear
php artisan cache:clear
php artisan view:clear
Code Quality Tools
- Laravel Pint - Code formatting based on Laravel preset with strict rules
- PHPStan (Level 5) - Static analysis with Larastan for Laravel-specific
checks
- Rector - Automated code modernization for PHP 8.4 and Laravel 12.0
- Cognitive Complexity - Limits complexity (class: 85, function: 8)
Development Tools
- Telescope: Laravel debugging assistant (enabled in testing)
- Log Viewer: Web-based log viewing interface
- IDE Helpers: Comprehensive IDE support with auto-generated helpers
- Xdebug: Available in Docker development environment
Pest
Testing
- If you need to verify a feature is working, write or update a Unit / Feature
test.
Pest Tests
- All tests must be written using Pest. Use
php artisan make:test --pest <name>.
- You must not remove any tests or test files from the tests directory without
approval. These are not temporary or helper files - these are core to the
application.
- Tests should test all of the happy paths, failure paths, and weird paths.
- Tests live in the
tests/Feature and tests/Unit directories.
- Pest tests look and behave like this:
it('is true',
function () { expect(true)->toBeTrue(); });
Running Tests
- Run the minimal number of tests using an appropriate filter before finalizing
code edits.
- To run all tests:
php artisan test.
- To run all tests in a file:
php artisan test tests/Feature/ExampleTest.php.
- To filter on a particular test name:
php artisan test --filter=testName
(recommended after making a change to a related file).
- When the tests relating to your changes are passing, ask the user if they
would like to run the entire test suite to ensure everything is still passing.
Pest Assertions
- When asserting status codes on a response, use the specific method like
assertForbidden and assertNotFound instead of using assertStatus(403) or
similar, e.g.: $response->assertSuccessful(); });
Mocking
- Mocking can be very helpful when appropriate.
- When mocking, you can use the
Pest\Laravel\mock Pest function, but always
import it via use function Pest\Laravel\mock; before using it.
Alternatively, you can use $this->mock() if existing tests do.
- You can also create partial mocks using the same import or self method.
Datasets
- Use datasets in Pest to simplify tests which have a lot of duplicated data.
This is often the case when testing validation rules, so consider going with
this solution when writing tests for validation rules.
Pest 4
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing,
visual regression testing, test sharding, and faster type coverage.
- Browser testing is incredibly powerful and useful for this project.
- Browser tests should live in
tests/Browser/.
- Use the
search-docs tool for detailed guidance on utilizing these features.
Browser Testing
- You can use Laravel features like
Event::fake(), assertAuthenticated(),
and model factories within Pest v4 browser tests, as well as RefreshDatabase
(when needed) to ensure a clean state for each test.
- Interact with the page (click, type, scroll, select, submit, drag-and-drop,
touch gestures, etc.) when appropriate to complete the test.
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
- If requested, test on different devices and viewports (like iPhone 14 Pro,
tablets, or custom breakpoints).
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging when appropriate.
Example Tests
$this->actingAs(User::factory()->create());
$page = visit('/sign-in'); // Visit on a real browser...
$page->assertSee('Sign In')
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!')
Notification::assertSent(ResetPassword::class);
});
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
Test Enforcement
- Every change must be programmatically tested. Write a new test or update an
existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use
php artisan test with a specific filename or filter.
Laravel Pint Code Formatter
- You must run
vendor/bin/pint --dirty before finalizing changes to ensure
your code matches the project's expected style.
- Do not run
vendor/bin/pint --test, simply run vendor/bin/pint to fix any
formatting issues.
1---2name: laravel-tester3description: Generates modern maintainable Laravel applications unit and feature (integration) tests with a focus on performance by default and for best testing style practices.4---5
6# Laravel Tester
7
8## Instructions
9
10### Models Testing Policy
11
12- DO NOT create unit tests for Laravel Eloquent models.
13- Rationale:
14 - Laravel's Eloquent ORM is extensively tested by the Laravel team
15 - Testing basic CRUD operations, relationships, and standard functionality
16 provides no value
17 - Models are excluded from code coverage metrics (see phpunit.xml)
18- What NOT to test:
19 - Basic relationships (hasOne, hasMany, belongsTo, etc.)
20 - Simple CRUD operations (create, update, delete, find)
21 - Standard Eloquent functionality
22 - Factory creation without custom logic
23 - Basic fillable/guarded attributes
24 - Standard casting functionality
25- Exceptions — What TO test:
26 - Custom business logic methods
27 - Complex accessors/mutators with business rules
28 - Custom scopes with specific logic
29 - Observer behavior and side effects
30 - Mass assignment protection (if critical)
31- Where to test model functionality instead:
32 - Feature tests via HTTP endpoints and workflows
33 - Integration tests for model interactions
34 - Observer tests for event handlers
35 - Action/Service tests for business logic
36
37### Framework
38
39- **Pest PHP** - Modern testing framework with BDD-style syntax
40- **Mutation Testing** with Infection for test quality assurance
41- **Architectural Testing** to enforce code structure rules
42
43### Test Structure
44
45```
46tests/
47├── Feature/ # Integration tests
48│ ├── Auth/
49│ ├── MentorPrograms/
50│ └── Pages/
51├── Unit/ # Unit tests
52│ ├── Actions/
53│ ├── Models/
54│ ├── Observers/
55│ └── Support/
56├── Pest.php # Pest configuration
57└── TestCase.php # Base test case
58```
59
60### Running Tests
61
62All tests must be run in a Docker container. Feature tests do not need to
63mutate.
64
65#### With Docker
66
67```bash
68# Run all tests
69docker compose exec app ./vendor/bin/pest
70
71# Run with coverage
72docker compose exec app ./vendor/bin/pest --coverage
73
74# Run mutation testing
75docker compose exec app ./vendor/bin/pest --mutate --covered-only --parallel --min=100
76
77# Run specific test file
78docker compose exec app ./vendor/bin/pest tests/Unit/ExampleTest.php
79```
80
81#### Local Environment
82
83```bash
84# Ensure test database is configured in .env.testing
85./vendor/bin/pest
86./vendor/bin/pest --coverage
87./vendor/bin/pest --mutate --covered-only --parallel --min=100
88```
89
90### Test Configuration
91
92- **Database**: Uses RefreshDatabase trait for clean test state
93- **Environment**: Configured in phpunit.xml with testing-specific settings
94- **Coverage**: Reports generated in `coverage/` directory
95- **Memory Limit**: 512M for test execution
96
97### Writing Tests
98
99#### Basic Test Structure
100
101```php
102<?php
103
104declare(strict_types=1);
105
106// For mutation testing (optional)
107mutates(YourClass::class);
108
109describe('Feature Description', function (): void {
110 beforeEach(function (): void {
111 // Setup code
112 $this->user = User::factory()->create();
113 });
114
115 it('describes what it tests', function (): void {
116 $result = someFunction();
117
118 expect($result)->toBe('expected_value');
119 });
120});
121```
122
123#### Testing Actions (Laravel Actions Pattern)
124
125```php
126it('handles the action correctly', function (): void {
127 $action = new YourAction();
128 $result = $action->handle($request, $parameters);
129
130 expect($result)
131 ->toBeInstanceOf(RedirectResponse::class)
132 ->and($result->getTargetUrl())
133 ->toBe(route('expected.route'));
134});
135```
136
137### Architectural Testing
138
139The project enforces architectural rules via `tests/Unit/ArchTest.php`:
140
141- No debugging functions in production code
142- Models must extend Eloquent Model
143- Page actions must have 'Page' suffix
144- Enums must be proper enum classes
145
146### Common Commands
147
148```bash
149# Code quality checks
150./vendor/bin/phpstan analyse
151./vendor/bin/pint --test
152./vendor/bin/rector process --dry-run
153
154# Fix code issues
155./vendor/bin/pint
156./vendor/bin/rector process
157
158# Generate IDE helpers
159php artisan ide-helper:generate
160php artisan ide-helper:models
161
162# Clear caches
163php artisan config:clear
164php artisan cache:clear
165php artisan view:clear
166```
167
168### Code Quality Tools
169
1701. **Laravel Pint** - Code formatting based on Laravel preset with strict rules
1712. **PHPStan (Level 5)** - Static analysis with Larastan for Laravel-specific
172 checks
1733. **Rector** - Automated code modernization for PHP 8.4 and Laravel 12.0
1744. **Cognitive Complexity** - Limits complexity (class: 85, function: 8)
175
176### Development Tools
177
178- **Telescope**: Laravel debugging assistant (enabled in testing)
179- **Log Viewer**: Web-based log viewing interface
180- **IDE Helpers**: Comprehensive IDE support with auto-generated helpers
181- **Xdebug**: Available in Docker development environment
182
183## Pest
184
185### Testing
186
187- If you need to verify a feature is working, write or update a Unit / Feature
188 test.
189
190### Pest Tests
191
192- All tests must be written using Pest. Use
193 `php artisan make:test --pest <name>`.
194- You must not remove any tests or test files from the tests directory without
195 approval. These are not temporary or helper files - these are core to the
196 application.
197- Tests should test all of the happy paths, failure paths, and weird paths.
198- Tests live in the `tests/Feature` and `tests/Unit` directories.
199- Pest tests look and behave like this:
200 <code-snippet name="Basic Pest Test Example" lang="php"> it('is true',
201 function () { expect(true)->toBeTrue(); }); </code-snippet>
202
203### Running Tests
204
205- Run the minimal number of tests using an appropriate filter before finalizing
206 code edits.
207- To run all tests: `php artisan test`.
208- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`.
209- To filter on a particular test name: `php artisan test --filter=testName`
210 (recommended after making a change to a related file).
211- When the tests relating to your changes are passing, ask the user if they
212 would like to run the entire test suite to ensure everything is still passing.
213
214### Pest Assertions
215
216- When asserting status codes on a response, use the specific method like
217 `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or
218 similar, e.g.:
219 <code-snippet name="Pest Example Asserting postJson Response" lang="php">
220 it('returns all', function () { $response = $this->postJson('/api/docs', []);
221
222 $response->assertSuccessful(); }); </code-snippet>
223
224### Mocking
225
226- Mocking can be very helpful when appropriate.
227- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always
228 import it via `use function Pest\Laravel\mock;` before using it.
229 Alternatively, you can use `$this->mock()` if existing tests do.
230- You can also create partial mocks using the same import or self method.
231
232### Datasets
233
234- Use datasets in Pest to simplify tests which have a lot of duplicated data.
235 This is often the case when testing validation rules, so consider going with
236 this solution when writing tests for validation rules.
237
238<code-snippet name="Pest Dataset Example" lang="php">
239it('has emails', function (string $email) {
240 expect($email)->not->toBeEmpty();
241})->with([
242 'james' => 'james@laravel.com',
243 'taylor' => 'taylor@laravel.com',
244]);
245</code-snippet>
246
247## Pest 4
248
249- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing,
250 visual regression testing, test sharding, and faster type coverage.
251- Browser testing is incredibly powerful and useful for this project.
252- Browser tests should live in `tests/Browser/`.
253- Use the `search-docs` tool for detailed guidance on utilizing these features.
254
255### Browser Testing
256
257- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`,
258 and model factories within Pest v4 browser tests, as well as `RefreshDatabase`
259 (when needed) to ensure a clean state for each test.
260- Interact with the page (click, type, scroll, select, submit, drag-and-drop,
261 touch gestures, etc.) when appropriate to complete the test.
262- If requested, test on multiple browsers (Chrome, Firefox, Safari).
263- If requested, test on different devices and viewports (like iPhone 14 Pro,
264 tablets, or custom breakpoints).
265- Switch color schemes (light/dark mode) when appropriate.
266- Take screenshots or pause tests for debugging when appropriate.
267
268### Example Tests
269
270<code-snippet name="Pest Browser Test Example" lang="php">
271it('may reset the password', function () {
272 Notification::fake();
273
274 $this->actingAs(User::factory()->create());
275
276 $page = visit('/sign-in'); // Visit on a real browser...
277
278 $page->assertSee('Sign In')
279 ->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
280 ->click('Forgot Password?')
281 ->fill('email', 'nuno@laravel.com')
282 ->click('Send Reset Link')
283 ->assertSee('We have emailed your password reset link!')
284
285 Notification::assertSent(ResetPassword::class);
286
287}); </code-snippet>
288
289<code-snippet name="Pest Smoke Testing Example" lang="php">
290$pages = visit(['/', '/about', '/contact']);
291
292$pages->assertNoJavascriptErrors()->assertNoConsoleLogs(); </code-snippet>
293
294## Test Enforcement
295
296- Every change must be programmatically tested. Write a new test or update an
297 existing test, then run the affected tests to make sure they pass.
298- Run the minimum number of tests needed to ensure code quality and speed. Use
299 `php artisan test` with a specific filename or filter.
300
301## Laravel Pint Code Formatter
302
303- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure
304 your code matches the project's expected style.
305- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any
306 formatting issues.