Generated by boost:update for Boost 2.8, which replaces the pest-testing skill with testing-best-practices and adds infer-conventions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017XYnWFt9pJEwvAmNFN38XD
3.8 KiB
Fakes, Mocks, and Determinism
Tests that depend on actual time, randomness, sleeping, or network calls can fail for reasons unrelated to the code under test. Control all four.
How to Isolate a Dependency
Fetch https://laravel.com/framework/docs/mocking for Laravel's fakes, facade doubles, and fake assertions. Confirm each name before using it.
Identify the dependency, then choose the first applicable option. A framework fake preserves the real code path, while a mock replaces the dependency.
- Always use framework fakes for facades such as events, queues, mail, notifications, storage, the HTTP client, time, and sleep.
- Use a developer-defined fake implementation of a service if the application provides one.
- Use a mock for a container-resolved contract only when the real implementation leaves the process or is nondeterministic.
- Use the real implementation for everything else, including the database.
Framework Fakes
- Create each fake inside the test that needs it. Do not create fakes in a file-level
beforeEach(). - Pass class names to
Event::fake()andQueue::fake()when you know which classes the code dispatches. A fake without class names can hide an unexpected dispatch. - Use a fake without class names only when the test asserts the complete result, including a call to
assertNothingPushed(). - Write one assertion for each fake. The assertion states that the code dispatches the item, or that the code does not dispatch the item.
- Assert the data of a job or of an event if that data is part of the behavior.
- Use
Exceptions::fake()to assert that the application reports the correct exception. Do not usewithoutExceptionHandling(), because it changes the response under test.
Create prerequisite factory records before calling Event::fake(). Factories use model events, such as a creating hook that generates a UUID, and a fake without class names suppresses those events and can produce an invalid model. Call the fake first only when a factory event is under test, and pass that event's class name.
Mocking
Use shouldReceive() before the action to declare an expectation. Use shouldHaveReceived() after the action for a spy. Use Mockery::on() or withArgs() if an equality check cannot state the expected argument, such as a check of one field of a value object.
Import the mock function before you use it: use function Pest\Laravel\mock;.
Outbound HTTP Testing
Call Http::preventStrayRequests(). Any request without a matching fake then fails without reaching the network.
Fake the exact endpoint used by each test. Do not call Http::fake() without an endpoint because it accepts unexpected requests and can hide defects.
Time and Randomness
- Freeze the time or move the time in each test that depends on a date, a period, or a timestamp.
- Use the framework helpers
freezeTime(),travelTo(),travel(), andtravelBack(). Do not callCarbon::setTestNow(). - Use
Str::createRandomStringsUsing()to fix a generated string, if the test asserts an identifier or a slug. - Use
Sleep::fake()instead of a real sleep, and assert the sleeps that the code requests. - Restore the time and the randomness after each test, if the suite does not restore them for every test.
Database
- Run real queries against the real records in the test database. Do not mock the query builder, because the test then asserts the mock.
- Assert the exact keys of
toArray()if the shape of the serialized model is a contract. The test then fails when the model exposes a new attribute. - Test application behavior caused by the schema, such as deleting dependent records through a cascade. Do not test the database engine's cascade implementation.
- Use
LazilyRefreshDatabaseinstead ofRefreshDatabase. A test that does not use the database then does not run the migrations.