7

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', 'testLogin@gmail.com')
                    ->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' => 'testLogin@gmail.com']);


    }

    /**
     * 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', 'testLogin@gmail.com')
                ->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' => 'testLogin@gmail.com']);
    }

    /**
     * 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', 'testLoginPass@gmail.com')
                ->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' => 'testLoginPass@gmail.com']);
    }
}
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
Jennifer Zhou
  • 323
  • 1
  • 7
  • 20

8 Answers8

20

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

wheelmaker
  • 2,975
  • 2
  • 21
  • 32
  • I added the line "use DatabaseTransactions" , but got this error "Fatal error: Trait 'Tests\Browser\DatabaseTransactions' not found in /Users/jenniferzhou/Documents/Cypress/MarketPlace/tests/Browser/UserRegisterTest.php on line 17". – Jennifer Zhou Aug 14 '18 at 22:31
  • Did you include the top use statement `use Illuminate\Foundation\Testing\DatabaseTransactions;` as well? This should go up above the class declaration. Then the `use DatabaseTransactions;` statement should go just inside the opening class line as demonstrated above. – wheelmaker Aug 16 '18 at 02:07
  • I added the line and now I do not get anymore errors. However, when I created a test to register a user in my database and added the lines to use DatabaseTransactions, the database still stores the user at the end of the test. – Jennifer Zhou Aug 16 '18 at 14:27
  • Can you post your test class ? Is there an exception being thrown, some other early exit or is it running to completion? – wheelmaker Aug 17 '18 at 16:15
  • I added a test that I tried to use with Database Transaction! – Jennifer Zhou Aug 19 '18 at 17:34
  • So it appears this trait only works on the default database connection. If you are using the default database connection, please double check that you have cleared the test data out of the database manually first and then re-test. Otherwise, if you must use a non default database connection, it appears Laravel does'n provide you with a solution for this. – wheelmaker Sep 13 '18 at 19:55
5

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.

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
3

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!

Bart
  • 1,889
  • 1
  • 21
  • 38
1

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;

}
1

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'];
}
Sai
  • 539
  • 6
  • 7
1

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:"/>
0

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

  • Yes, this is a great package, but I don't think it is the main purpose for this kind of usage, How about cancelled tests in the midlle of the job? Is it covering of all requested scenarios? Certainly not. And still there are some artisan commands which you nieed to invoke. So isn't just easier to modify your `.env` to match proper test db and than switch back? – Bart Aug 10 '18 at 01:25
0

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";
Yevgeniy Afanasyev
  • 37,872
  • 26
  • 173
  • 191