Search code examples
phplaraveltestinglaravel-5phpunit

Laravel saving to database when running unit tests


Im attempting to run a Laravel functional test, using the Model Factory to create some test data.

On other tests, within the same directory and namespace, using similar 'make' traits, no entries are saved in the database. But with this one they are.

When using app('env') it returns 'testing', so the environment is correct.

Any ideas on how I can run these tests without saving to the mySQL database?

RoleTest

namespace Tests;

use Tests\Traits\MakeRoleTrait;

class RoleTest extends BrowserKitTestCase
{
    use MakeRoleTrait;

    public function testGetChildren()
    {
        $childRoles = [];
        $baseRoles = [];

        for ($i = 0; $i < 4; $i++) {
            $parentId = null;

            if($i > 0){
                $parentId = $baseRoles[$i-1]->id;
            }

            $childRole = $this->makeRole();

            // Create base role
            $baseRoles[] =  $this->makeRole([
                'parent_id' => $parentId,
                'display_name' => $childRole->display_name,
                'site_id' => null
            ]);

            if($i < 3){
                $childRoles[] = $childRole;
            }
        }

        $children = $childRoles[0]->getChildren();

        $this->assertEquals($childRoles,$children);
    }
}

MakeRoleTrait

namespace Tests\Traits;


use Faker\Factory as Faker;
use App\Models\Role;
use App\Repositories\RoleRepository;

trait MakeRoleTrait
{
    /**
     * Create fake instance of Role and save it in database
     *
     * @param array $roleFields
     * @return Role
     */
    public function makeRole($roleFields = [])
    {
        /** @var RoleRepository $roleRepo */
        $roleRepo = \App::make(RoleRepository::class);
        $theme = $this->fakeRoleData($roleFields);
        return $roleRepo->skipPresenter()->create($theme);
    }

    /**
     * Get fake instance of Role
     *
     * @param array $roleFields
     * @return Role
     */
    public function fakeRole($roleFields = [])
    {
        return new Role($this->fakeRoleData($roleFields));
    }

    /**
     * Get fake data of Role
     *
     * @param array $roleFields
     * @return array
     */
    public function fakeRoleData($roleFields = [])
    {
        $role = factory(\App\Models\Role::class,1)->make();
        $array = $role->first()->toArray();

        $array = array_merge(
            $array,
            $roleFields
        );

        return $array;
    }
}

Solution

  • Laravel has traits to roll-back changes made to the database. In 5.6, it's the RefreshDatabase trait - in some earlier versions it was DatabaseTransactions instead.

    These traits should be added to all tests that make database updates/inserts.

    (For additional ease (and safety), tests should run on a separate database connection with a separate copy of the database, too.)