Jennifer Zhou
Jennifer Zhou

Reputation: 343

Laravel Reset Database after Test

I have just started using Laravel Dusk to test my project and need some guidance. After I run all the tests available, I want to be able to reset my database back to before I run the tests. (If there were any entries in my database before I run the tests, I would still like to see them after I run the tests. However, any entires created during the test, I would not like to see them after the tests finish running.) Any pointers on how I would achieve this? Thank you!

Update:

<?php

namespace Tests\Browser;

use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserRegisterTest extends DuskTestCase
{
    use DatabaseTransactions;
    /**
     * A test for user registration.
     * @group register
     * @return void
     */

    public function testRegisterUser()
    {
        //Register with all info filled out correctly
        $this->browse(function ($browser){
            $browser->visit('/register')
                    ->type('firstName', 'JenLogin')
                    ->type('lastName', 'Zhou')
                    ->type('email', '[email protected]')
                    ->type('bio', 'Hello, this user is for testing login purposes!')
                    ->type('location_zip', '11111')
                    ->type('password', '123456')
                    ->type('password_confirmation', '123456')
                    ->click('.btn-primary')
                    ->assertPathIs('/home')
                    ->click('.dropdown-toggle')
                    ->click('.dropdown-menu li:last-child');

        });

        $this->assertDatabaseHas('users', ['firstName' => 'JenLogin', 'lastName' => 'Zhou', 'email' => '[email protected]']);


    }

    /**
     * Register with duplicate user
     * @group register
     * @return void
     */
    public function testRegisterDuplicateUser(){

        $this->browse(function ($browser){
            $browser->visit('/register')
                ->type('firstName', 'JenLoginDup')
                ->type('lastName', 'Zhou')
                ->type('email', '[email protected]')
                ->type('bio', 'Hello, this user is for testing login purposes!')
                ->type('location_zip', '11111')
                ->type('password', '123456')
                ->type('password_confirmation', '123456')
                ->click('.btn-primary')
                ->assertPathIs('/register')
                ->assertSee('The email has already been taken.');
        });

        $this->assertDatabaseMissing('users', ['firstName' => 'JenLoginDup', 'lastName' => 'Zhou', 'email' => '[email protected]']);
    }

    /**
     * Register with incorrect password confirmation
     * @group register
     * @return void
     */
    public function testRegisterUserNoPassConfirm(){

        $this->browse(function ($browser){
            $browser->visit('/register')
                ->type('firstName', 'JenLoginPass')
                ->type('lastName', 'Zhou')
                ->type('email', '[email protected]')
                ->type('bio', 'Hello, this user is for testing login purposes!')
                ->type('location_zip', '11111')
                ->type('password', '123456')
                ->type('password_confirmation', '888888')
                ->click('.btn-primary')
                ->assertPathIs('/register')
                ->assertSee('The password confirmation does not match.');
        });

        $this->assertDatabaseMissing('users', ['firstName' => 'JenLoginPass', 'lastName' => 'Zhou', 'email' => '[email protected]']);
    }
}

Upvotes: 9

Views: 31297

Answers (8)

Oussama ELJabbari
Oussama ELJabbari

Reputation: 115

I had a similar problem, all I had to do is just uncomment these two lines in phpunit.xml:

<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

Upvotes: 2

Sai
Sai

Reputation: 618

For multiple databases, this helped me

class MyTest extends TestCase {
    // Reset the DB between tests
    use DatabaseTransactions;

    // Setting this allows both DB connections to be reset between tests
    protected $connectionsToTransact = ['mysql', 'myOtherConnection'];
}

Upvotes: 1

Amir Pourastarabadi
Amir Pourastarabadi

Reputation: 23

You can use the RefreshDatabase trait in your test classes.After each test the database will be like before test. In Fact it will drop all tables and migrate again. If you would not loose your data you can use one separate schema for test.

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

}

Upvotes: 2

Yevgeniy Afanasyev
Yevgeniy Afanasyev

Reputation: 41430

phpunit.xml file is your solution there, you can set a .env variables in this file like so

    <env name="DB_CONNECTION" value="testing_mysql"/>
    <env name="DB_DATABASE_TEST" value="test"/>

now you can run your tests on a separate database.

Plus you can run a .php file every time before tests in automation, you just need to tell it to unittests

<phpunit 
     ...
     bootstrap="tests/autoload.php"
>

You can put any cleaners or seeders there or something like

echo 'Migration -begin-' . "\n";
echo shell_exec('php artisan migrate:fresh --seed');
echo 'Migration -end-' . "\n";

Upvotes: 1

whmkr
whmkr

Reputation: 3085

You are looking for the DatabaseTransactions trait. Use it in your test class like this and it will automatically rollback all database transactions made during your tests.

use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use DatabaseTransactions;

    // test methods here
}

This will keep track of all transactions made during your test and undo them upon completion.

note: this trait only works on default database connections

Upvotes: 23

Marcin Nabiałek
Marcin Nabiałek

Reputation: 111889

First of all, when you are running tests you should use completely different database than your live (or dev) database. For this you should create .env.dusk and set in there:

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=testing_database
DB_USERNAME=root
DB_PASSWORD=pass

to database used for tests only.

Second thing is that for Laravel Dusk you cannot use just DatabaseTransactions. You should in fact use DatabaseMigrations for Dusk tests otherwise you will get unexpected results.

Upvotes: 6

foomasri
foomasri

Reputation: 1

I think this is a great question. I found an Artisan tool that may be what you are looking for. You can use it to take a snapshot of the database before you run the test and then use it again to load that snapshot restoring your database to the previous state. I gave it a run(using MYSQL) and it worked great. Hope this is what you are looking for. Here is a link...

https://github.com/spatie/laravel-db-snapshots

Upvotes: 0

Bart
Bart

Reputation: 1957

There is no sane workflow for running tests on live/dev db with data and reverting changes back, done by tests.

Therefore your approach fails here, instead you should:

  1. Create separate test schema/db for tests
  2. Switch to test db, before running tests - this can be somehow automated depending on your configuration in phpunit and .env.dusk, but it depends on your local setup.
  3. Then in your tests you will create all from scratch on clean db (run migrations, seeds, factories)
  4. Run tests against this test db
  5. For development switch back to your base db with current data, which will not be affected by tests.

Next time you will run your tests all starts again from point zero - clean database, this will be done by in tests: use CreatesApplication; use DatabaseMigrations; parent::setUp();etc.

Read more about these methods...

Side Notes:

  1. With this approach, it will be easy, to test your app in CI environments also.

  2. Never write your tests which depend on data on your dev/live db. For tests all required data should be provided by seeds or ewentually factories!

Upvotes: 3

Related Questions