laravel 5.8 turn off model save method during test

213 Views Asked by At

I want to test a service who change the state of a model.

this is my model :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Color extends Model
{
    protected $table = 'color';
    protected $connection = 'someconnection';
    public $timestamps = false;
}

and this is the service :

<?php

namespace App\Services;

use App\Models\Color;

class ColorChanger
{
    public function changeColor(Color $color): void
    {
        switch($color->name) {
            case 'green':
                $color->name = 'red';
                break;
            case 'red':
                $color->name = 'orange';
                break;
        }
        $color->save();
    }
}

I tried to make a simple test like this :

<?php

namespace Tests\Unit\Services;

use App\Models\Color;
use App\Services\ColorChanger;

use Tests\TestCase;

class ColorChangerTest extends TestCase
{
    public function test_color_has_changed_from_green_to_red()
    {
        $color = new Color();
        $color->name = 'green';
        $colorChanger = new ColorChanger();
        $colorChanger->changeColor($color);
        $this->assertEquals('red', $color->name);
    }
}

but when I run the test it gives me an error because the save method is trying to update the database. I am in a test environment and there is no database available. I tried factories, I tried to mock the Color Model to prevent the save method to connect to the database, but in vain. I ended up to use the repository pattern, I inject it into the service ColorChanger and mock its save method during the test.

class ColorRepo
{
    public function save(Color $color): bool
    {
        return $color->save();
    }

class ColorChanger
{
    private ColorRepo $colorRepo;

    public function __construct(ColorRepo $colorRepo): void
    {
        $this->colorRepo = $colorRepo;
    }

    public function changeColor(Color $color): void
    {
        switch($color->name) {
            case 'green':
                $color->name = 'red';
                break;
            case 'red':
                $color->name = 'orange';
                break;
        }
        $this->colorRepo->save($color);
    }
}

Isn't there another simple way to turn off the models save method ? Because using the repository pattern adds extra codes which looks to me really too much for this simple case.

1

There are 1 best solutions below

0
On

I'm thinking about the creation of a class called Mockable or something of the like that would look like this:

class Mockable {
    public static $isDebug = false;
    public static function save($repo, $entity) : void
    {
        if ($isDebug) $repo->save($entity);
    }
}

and call it like

Mockable::save($this->colorRepo, $color);

of course, you will need to change $isDebug to true if you are debugging.