Should a factory always define a required field?

Sometimes a little extra manual work makes for a better test

Joel Clermont
Joel Clermont
2024-05-08

I've been working on a project introducing Laravel to a .NET application that is over 20 years old. The database is running on MS SQL Server and the schema is way outside the normal Laravel conventions. It has been quite interesting, to say the least.

Generally, our rule for writing factories is to keep the default state as minimal as possible. If a field is nullable, we don't specify it. But this also means that every non-null field is specified as part of the default definition.

In this legacy schema, we had a field used as if it were a primary key (it even had ID in the name), but it was not set up as an auto incrementing field. It did not allow nulls, and it had to be unique. How would we handle this in the factory?

My first version had something like this in the factory definition: 'ProductID' => Product::max('ProductID') + 1.

This works, and it's easy to understand, but it felt weird. In fact, Aaron pushed back during code review and we brainstormed a different approach.

The fundamental issue was bad schema design. I was trying to "paper over" this poor schema design and keep the factory useful from a developer perspective. So while I made it feel more like a normal factory, I now let some of the weirdness of the schema leak into our Laravel application. This is how technical debt starts to spread.

Instead, we decided to deviate from our normal way of writing factories and leave a required field unspecified in the default definition. This avoided the weird code in the factory, but it also means that every time we use it, we also need to manually provide the ProductID.

When I implemented this change, I realized all but one of our tests was already passing it in, so it ended up being quite simple.

Working in legacy applications or on legacy database schemas is an interesting balancing act. You want to make things better, but you can't always code things the way you might normally prefer.

Hope this helps,

Joel

P.S. I honestly like working on thorny, legacy projects. If you have one and need some help getting it under control, let's talk.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼

Level up your Laravel skills!

Each 2-minute email has real-world advice you can use.