Challenge 3: Diagnose a Test Suite Missing RefreshDatabase — Possible Solution ==================================================================== // BROKEN — extends Tests\TestCase but never uses RefreshDatabase namespace Tests\Feature; use App\Models\Book; use Tests\TestCase; class RollbackDemoTest extends TestCase { // ⚠️ missing: use RefreshDatabase; public function test_a_creates_a_book(): void { Book::factory()->create(); $this->assertEquals(1, Book::count()); } public function test_b_sees_no_books(): void { $this->assertEquals(0, Book::count()); } } // FIXED namespace Tests\Feature; use App\Models\Book; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class RollbackDemoTest extends TestCase { use RefreshDatabase; public function test_a_creates_a_book(): void { Book::factory()->create(); $this->assertEquals(1, Book::count()); } public function test_b_sees_no_books(): void { $this->assertEquals(0, Book::count()); } } WHY test_b_sees_no_books MIGHT FAIL WITHOUT THE FIX -------------------------------------------------------- Without RefreshDatabase, this test class has NO automatic mechanism resetting the database between test methods — extending Tests\TestCase alone (unlike Django's TestCase, which wraps every test in a rollback transaction automatically just by inheritance) provides no such protection on its own in Laravel. If test_a_creates_a_book runs first (PHPUnit's default execution order is typically the order methods are defined in the file, though this can vary with configuration), it creates a real Book row that is NEVER CLEANED UP — it just sits in the actual test database. When test_b_sees_no_books runs afterward, Book::count() finds that leftover row from the previous test and returns 1, not 0 — causing an assertion failure ("Failed asserting that 1 matches expected 0") that has NOTHING to do with any actual bug in the application code; it's purely a symptom of test pollution. This is exactly the flaky-test scenario the chapter's gotcha describes: the failure is non-obvious, depends entirely on execution order (running test_b_sees_no_books in isolation, or before test_a_creates_a_book, would pass just fine), and traces back to one missing trait rather than any real logic error. Adding `use RefreshDatabase;` fixes this by wrapping EACH test method in its own database transaction that's rolled back automatically when that method finishes — Book::factory()->create() from test_a_creates_a_book is undone before test_b_sees_no_books ever runs, so Book::count() correctly returns 0 regardless of execution order. WHY THIS WORKS -------------- - This is the Laravel-specific demonstration of exactly the same principle proven for Django's TestCase in the Django project's equivalent challenge — the difference here is that Laravel's version of this safety property is OPT-IN (via the trait) rather than automatic, which is precisely why forgetting it produces real, observable test pollution instead of simply being redundant.