The problem you're running into is related to the way Laravel handles cache during testing, especially when using the RefreshDatabase trait and custom cache stores that use the database driver.
Why the Cache Does Not Persist
- Separate Database Connections: When your tests run, the default database connection is often set to a special SQLite testing database (often in-memory, though you say you're using a real SQLite DB file). The
Cachestore you've defined (with the'persistent'key) will by default use the database and table you configure, but it may not be using the same connection/transaction as your main DB connection within tests. - Transactions & Caching: The
RefreshDatabasetrait wraps each test in a transaction and rolls it back at the end. The database cache driver does not participate in these transactions for cache tables—even if it's using the same connection—so cache writes in one process (e.g., your test setup) might not be visible in another (like the Livewire component's process). - Serialization/Persistence: Also, cache drivers serialize data. If you're caching an object like
new SignUpAttempt(...), it must be serializable and autoloadable in both the test and component context. If there are differences or issues in serialization during testing, the object may not fully persist.
What Is Happening
- You write to the cache in your test with
Cache::store('persistent')->put(...). - The Livewire component (running in a separate HTTP request context, simulated by Livewire) tries to read from the same cache.
- Because of either connection separation, DB transaction/rollbacks, or object serialization, the component can’t read the cache entry.
- Hence, your event never fires.
Why Using DB Facade Directly Works
- When using
DB::table(...), you directly manipulate the table and sidestep cache driver logic and serialization, ensuring both the test and Livewire component can read/write as expected, assuming they're on the same connection.
Solution
1. Ensure the Cache is Using the Test Database Connection
Make sure your persistent cache store uses the same connection as your tests. In phpunit.xml, you probably set DB_CONNECTION=sqlite. You should make sure that both the main and cache connections use the same database file (or in-memory database, but beware of its isolation in separate processes).
Example .env.testing or phpunit.xml snippet:
DB_CONNECTION=sqlite
DB_DATABASE=/full/path/to/database/database.sqlite
DB_CACHE_CONNECTION=sqlite
DB_CACHE_DATABASE=/full/path/to/database/database.sqlite
And in config/cache.php:
'persistent' => [
'driver' => 'database',
'connection' => env('DB_CACHE_CONNECTION', 'sqlite'),
'table' => 'persistent_cache',
...
],
Tip: Dump the database connection in both test and component to confirm they're the same.
2. Serialize Your Objects with Cache
If you cache an object (SignUpAttempt), make sure it and any dependencies are properly serializable, and ensure all related classes are included in the test process and Livewire context.
For testing, prefer using simple data structures (arrays) if possible.
3. Workaround for Tests: Use Cache Fake in Testing
One way to make your test simpler and avoid DB-based cache persisting issues is to use the Cache fake. You'll mock the cache methods for the duration of your test, ensuring the test and the component use the same in-memory cache.
Add this at the beginning of your test:
Cache::store('persistent')->flush();
Cache::store('persistent')->forget($cacheKey);
Or better: Use Laravel’s cache fake:
Cache::store('persistent')->flush();
Cache::shouldReceive('store')
->with('persistent')
->andReturnSelf();
But in general, by default, Laravel’s Cache::fake() only fakes the default cache store, so you can use this package or implement a custom fake for your named store. Otherwise, ensure both the test and the component are operating in the same context and on the same database.
4. Avoid Caching in Database in Test—Use Array Driver
For tests only, you could override the driver to use the in-memory array driver. In phpunit.xml or .env.testing:
CACHE_DRIVER=array
Or for your persistent store:
PERSISTENT_CACHE_DRIVER=array
And in your config/cache.php:
'persistent' => [
'driver' => env('PERSISTENT_CACHE_DRIVER', 'array'),
...
],
This will avoid any database persistence and test isolation problems—at the small cost of not testing the real backing store, but it’s very reliable for most unit/integration tests.
Summary: How to Fix Your Test
- Make sure your
'persistent'cache store is really using the same database and connection as the test. - For cache tests, prefer using the array cache (in-memory) for reliability, or use simple, serializable values (arrays) instead of objects in the cache.
- Flush/forget cache keys at the start of each test to avoid state leakage.
- Debug the raw contents of the
persistent_cachetable to confirm what’s being written/read.
Example: Recommended Test Setup
it('fires the UserSignedUp', function () {
Event::fake();
$user = User::factory()->withoutProfile()->create();
$cacheKey = sprintf(SignUpAttempt::CACHE_KEY_TEMPLATE, $user->getKey());
// Prefer array/simple structure for cache value
Cache::store('persistent')->put($cacheKey, ['foo' => 'bar']);
Livewire::actingAs($user)
->test(CompleteProfile::class)
->set('form.firstName', $firstName = fake()->firstName())
->set('form.lastName', $lastName = fake()->lastName())
->set('form.company', $company = fake()->company())
->set('form.jobTitle', $jobTitle = fake()->jobTitle())
->call('completeProfile')
->assertHasNoErrors();
Event::assertDispatched(UserSignedUp::class);
});
If you still have issues, try logging the output of Cache::store('persistent')->get($cacheKey) just before and inside the Livewire component for debugging.
Further Reading
Let me know if you need a more concrete example based on your actual connection/environment setup!