Laravel testing: models without database connection

I like to have unit tests that work without database connection so they can run on any situation and they can be run for example from local git hooks with no problem.

I use factories for this tests and the way to do it have changed a little for last Laravel versions (8, 9…) and it can get tricky so we’ll see how it works now.

We can start by creating the factories that we will use, in my case for the models Van and Brand, you only need to fill in the fields that you will use.

<?php
// database/factories/VanFactory.php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Van;

class VanFactory extends Factory
{
    protected $model = Van::class;

    public function definition()
    {
        return [
            'id' => 5,
            'name' => 'California',
        ];
    }
}
<?php
// database/factories/BrandFactory.php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Brand;

class BrandFactory extends Factory
{
    protected $model = Brand::class;

    public function definition()
    {
        return [
            'id' => 22,
            'name' => 'Volkswagen',
        ];
    }
}

We still need to update our models to make the factories work correctly.

<?php
// app/Models/Van.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;

class Van extends Model
{
    use HasFactory;
<?php
// app/Models/Brand.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;

class Brand extends Model
{
    use HasFactory;

If this is the first time we use factories, we may need to update our composer.json autoload section:

    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    },

And now we can start writing our first test, I will only show here the part where we instantiate the model without needing to access the database.

<?php
// tests/Unit/VanProcessingTest.php
namespace Tests\Unit;

use Tests\TestCase;
use App\Models\Van;

class VanProcessingTest extends TestCase
{
    public function test_van_brand_checking()
    {
        $van = Van::factory()->make();

        $this->assertEquals('Volkswagen', $van->brand->name);
    }

The problem here is that the test will try to access to the database as the $van don’t have the brand field filled. We need to link the van with its brand.

We can do this using the afterMaking method in configure:

<?php
// database/factories/VanFactory.php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Van;
use App\Models\Brand;

class VanFactory extends Factory
{
    protected $model = Van::class;

    public function configure()
    {
        return $this->afterMaking(function (Van $van) {
            if (! $van->brand_id) {
                $van->brand()->associate(Brand::factory()->make());
            }
        }
    }

    public function definition()
    {
        return [
            'id' => 5,
            'name' => 'California',
        ];
    }
}

Now we’ll have a functional model to test with no real database access.


Leave a Reply

Your email address will not be published. Required fields are marked *