Week 018 — Testing Code You Haven't Tested

Built a whole new class structure without writing a single test. Then tested it.

Week of March 17, 2025

Progress

Got Composer autoloading working with the new class structure on Monday. Then the actual debugging began.

The service layer was receiving a PDO object when it expected a repository. That mismatch required understanding why the distinction matters: a repository abstracts the database, exposing domain-level operations like findById() or save(). Passing a PDO directly skips that abstraction entirely. A Grok conversation helped clarify the reasoning — understanding SOLID principles at the level of "why would I actually do this" rather than just knowing the names.

By Thursday, updates were working but the lost item mechanism was broken — the lost_items record wasn't being created. Friday, the save logic for all three states — newly lost, already-lost updates, and safe — was working.

The Hard Part

Two bugs from the week that took longer than they should have:

First: copyWithLostItemId() was returning an object where lostItemId was still null despite the value being passed correctly. The property assignment in the constructor used $this->LostId instead of $this->lostId. Case mismatch, one character, invisible until you're staring at it looking for something else.

Second: the reward amount wasn't making it to the services layer when reporting an item lost. The ItemDTO didn't include the reward, and the save logic was routing everything through a single saveItem() method that built SQL from the DTO. The fix required pulling the lost reporting logic out of saveItem() entirely and into its own reportLost() function — which is the right design anyway. The reward just forced the issue earlier than planned.

Decision Made

Two this week. First: ItemDTO should allow null for the id field, since the DTO is often constructed before the item exists in the database. ID generation belongs in the service layer.

Second: ItemRepository.saveItem() should only handle updates. New items go through a separate insert path. The single-method approach that handled both was a legacy of writing code before the DTO pattern existed.

Learnings

Building a class structure without testing it means you find all the bugs at once, under conditions where it's hard to isolate which change broke what. Not ideal. Probably a good time to learn about unit testing.

Next Week

Lost and update saves are working. The found item path still needs attention.