Building a 1,378-Test Safety Net
From ~200 tests to 1,378 in a single push. The bugs found along the way proved the investment was already overdue.
I went from ~200 tests covering 19 controllers to 1,378 tests with 3,958 assertions covering the entire PlanPal app. What I found along the way proved the investment was already overdue.
The Bugs That Were Already There
Writing tests isn't just about preventing future bugs -- it catches existing ones:
A WebPush controller called successResponse() and errorResponse() on every endpoint -- methods that didn't exist on the base controller. Every WebPush endpoint would have returned a 500:
// Every method in WebPushController had this:
return $this->successResponse($data); // Method doesn't exist!
// BaseController actually has:
return $this->success($data);
A Book controller had an N+1 query -- loading activeSession for each book individually instead of eager loading with ->with('activeSession').
Appointment deletion left orphaned relationship goal completions, silently inflating a user's progress counters.
Email Intelligence skipped OAuth validation when granted_scopes contained gmail.readonly, even when the actual token didn't have that scope -- causing every scan to fail with a 403.
None of these were caught by manual testing. All caught immediately by automated tests.
The Coverage
| Category | Tests | Assertions |
|---|---|---|
| API Controllers (45 files) | ~960 | ~2,750 |
| Services (8 files) | ~180 | ~400 |
| Jobs (8 files) | ~50 | ~130 |
| Commands, Observers, Auth | ~188 | ~678 |
| Total | 1,378 | 3,958 |
Every API controller now has tests covering happy paths, authentication (401 for unauthenticated), user isolation (User A can't access User B's data), validation, and premium gates.
The Most Important Line
// AppServiceProvider.php
Model::preventLazyLoading(! $this->app->environment('production'));
One line. In development, any accidental lazy load throws LazyLoadingViolationException. In production, it still works normally. This single addition prevents every future N+1 query from shipping unnoticed.
The ROI
Every future change runs against 1,378 tests in ~62 seconds. Every bug fix gets a regression test. Every new feature has a template to follow. The best time to write tests was when the code was first written. The second best time is now.
Built while working on PlanPal.
Back to danielships ->