Using Laravel 8 Factory Classes For Non-Models

Nick Poulos
3 min readOct 3, 2020

I want to start off by saying I think Laravel provides a great set of testing tools out of the box. They really encourage test writing and make much easier to do so. Things like Factories and Seeders, traits like RefreshDatabase, MakesHTTPRequests, WithFaker, and things like the included Bus::fake() really make test writing a breeze using Laravel.

With that being said, there have been some recent changes to Factories in Laravel 8 that while overall are definitely an improvement and a step in the right direction, they seem to be more tightly coupled to Eloquent Models. Let me explain.

I like to use Factories for classes other than Eloquent models, especially something like a DTO (see here and here). In previous versions of Laravel (< 8) this was easy and straightforward to accomplish. We just pass in whatever class we want to our define() method.

Defining a Factory in previous versions of Laravel

Then later we would use this factory like so:

Using a factory during testing in a previous version of Laravel

But in Laravel 8, Factories are now proper classes. These classes have a $model var which tells the Factory which class this is for. This new method also requires that we add a HasFactory trait to the class that has the Factory.

If you look at the included App\Models\User.php in Laravel 8, you will see an example of this. For more information check out the official docs.

Unfortunately, if our class is not an Eloquent model, there is an extra step required that was not obvious at first glance.

First let’s setup our new class-based Factory, and set the ‘model’ var to our AdvertisementDto::class.

Setting up our class-based Factory, note the $model var is not really an Eloquent model

Now let’s add the HasFactory trait AND override one of its methods in our AdvertisementDto. We need to override the newFactory() method, and simply return a new instance of our factory.

Adding the HasFactory trait to our non-model class, and overriding its newFactory() method to return a new instance of our Factory in Laravel 8

At this point we should be all setup, and call our new non-model-factories in our tests like so:

Using our new non-model Factory in a Laravel 8 test

Alright well hopefully that saved somebody a few minutes of time, thanks for reading! If you see something I missed or could have done better, def let me know in the comments!

--

--