Laravel Dusk – Intuitive and Easy Browser Testing for All!

Viraj Khatavkar
Viraj Khatavkar
Share

This article was peer reviewed by Younes Rafie. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!


End to end testing for JavaScript applications, particularly single-page-apps, has always been a challenge. To that end, Laravel released its 5.4 version recently with a new testing library: Dusk.

Laravel Dusk Logo

With the release of Dusk, Laravel hopes to give its users a common API for browser testing. It ships with the default ChromeDriver, and if we need support for other browsers, we can use Selenium. It will still have this common testing API to cater to our needs.

The tutorial will assume you’re starting a new Laravel 5.4 app.

Installation

composer require laravel/dusk

This will install the most recent stable version of the package via Composer.

Next, we need to register DuskServiceProvider within our application. We can do it in a couple of ways:

Approach 1

We can include it in the providers array of our config/app.php file.

...

App\Providers\RouteServiceProvider::class,
Laravel\Dusk\DuskServiceProvider::class,
...

The problem with this approach is that DuskServiceProvider will be registered in our application for all the environments. We don’t need Dusk to be available and registered in our production environment. We can avoid this with the second approach.

Approach 2

Register DuskServiceProvider in the AppServiceProvider class for specific environments:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\DuskServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        if ($this->app->environment('local', 'testing', 'staging')) {
           $this->app->register(DuskServiceProvider::class);
        }
    }
}

Next, to complete the installation process:

php artisan dusk:install

Dusk will provide a basic scaffolding of classes and directories. If we open the tests directory, we can see a new Browser directory with the necessary scaffolding for Dusk tests.

Our First Test

First, we will scaffold an authentication workflow using Laravel’s pre-built authentication mechanism.

php artisan make:auth

Let us now create our first Dusk test:

php artisan dusk:make LoginTest

The above command will create a LoginTest class in our Browser directory.

class LoginTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_I_can_login_successfully()
    {
        $this->browse(function ($browser) {
            $browser->visit('/login')
                    ->type('email', 'viraj@virajkhatavkar.com')
                    ->type('password', 'secret')
                    ->press('Login')
                    ->assertSee('You are logged in!');
        });
    }
}

In the above test case, we check whether a user can successfully log into the system and see a home page with a welcome message.

Note: To make this test succeed, we need to have an actual user in the database. For this demonstration, we have already configured a user in the database with the above credentials.

Let us now execute our Dusk tests:

php artisan dusk

If you have a user entry in the database with the correct credentials, you can see the below output:

PHPUnit 5.7.6 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 4.71 seconds, Memory: 10.00MB

OK (2 tests, 2 assertions)

Failed Tests

When tests fail, PHPUnit throws some errors at us. We have to interpret what went wrong.

Dusk has a few notable additions in place to cater to this use-case.

We will first modify our test to deliberately fail on execution:

public function test_I_can_login_successfully()
{
    $this->browse(function ($browser) {
        $browser->visit('/login')
                ->type('email', 'viraj2438@gmail.com')
                ->type('password', 'secret')
                ->press('Login')
                ->assertSee('You are logged in!');
    });
}

In the above test case, we will try to log in with a user who is not present in the database. Let us now run our Dusk tests and see what happens:

Failed-Test

If you look carefully, a browser opens there just before the test fails.

Behind the scenes, Dusk takes a screenshot of the page where the error was triggered, and saves it in the (automatically git-ignored) screenshots directory:

Screenshot-Dusk-Test-Failed

This gives us a visual representation of why our test fails. We can see that the credentials were not matching any records in the database. With visual feedback like this, Dusk makes sure you can quickly pinpoint the problems.

Testing AJAX Calls

Dusk is meant for end to end browser testing of modern JavaScript applications. In such apps, a typical use-case is waiting for the response of AJAX requests.

We will test the Create Task feature from a demo app – please clone it to follow along:

Dusk - Create Task

We use an AJAX request to create a new task and then redirect the user to the task list page. This is a perfect testing use-case.

php artisan dusk:make CreateTaskTest

The above command will create a CreateTaskTest class in our Browser directory. Let us now write the test:

class CreateTaskTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_I_can_create_task_successfully()
    {
        $this->browse(function ($browser) {

            $browser->visit('/tasks/create')
                    ->type('title', 'My Task')
                    ->press('Add Task')
                    ->pause(5000)
                    ->assertPathIs('/tasks');
        });
    }
}

In the above test, we try to add a new test using the AJAX form:

  1. Enter the title
  2. Click the Add Task button
  3. Wait for 5 seconds
  4. Assert that we are redirected to the tasks page

Let us now run our Dusk tests and see the result:

AJAX Test Passed Dusk

As you can see, the above test passed.

We can also use the waitUntilMissing method of Dusk’s API to test the flow above:

<?php

namespace Tests\Browser;

use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class CreateTaskTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_I_can_create_task_successfully()
    {
        $this->browse(function ($browser) {

            $browser->visit('/tasks/create')
                    ->type('title', 'My Task')
                    ->press('Add Task')
                    ->waitUntilMissing('.btn-primary')
                    ->assertPathIs('/tasks');
        });
    }
}

You can refer to the official documentation for learning about other waiting elements available in the API.

A More Advanced Example

Our app has a menu item, wherein if we click on the Support Email link, a modal pops up with a form for sending a support email:

Open-Support-Modal

We will write a test case to test the following scenario:

  1. Log in
  2. See Support Email
  3. Click on Support Email
  4. Assert that modal opens and has user’s email ID in the text box

In the above gif, we interact with our UI using a mouse and open the modal. Let us re-create the above flow in our Dusk test case.

First, we will create a new test class:

php artisan dusk:make SupportEmailsTest

Then, the test:

class SupportEmailsTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_I_can_open_modal_for_support_emails()
    {
        $this->browse(function ($browser) {

            $user = factory(User::class)->create();

            $browser->loginAs($user)
                    ->visit('/tasks')
                    ->clickLink('Support Email')
                    ->whenAvailable('#modal-support', function ($modal) use($user) {
                        $modal->assertInputValue('#support-from', $user->email);
                    });
        });
    }
}

Let’s run it:

php artisan dusk tests/Browser/SupportEmailsTest.php

We can see that our test passes:

PHPUnit 5.7.13 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 3.63 seconds, Memory: 12.00MB

OK (1 test, 1 assertion)

For all other available assertions, you can refer to the official documentation.

Pages

There is a concept of Pages in Dusk. They are nothing more than powerful, reusable test classes.

Let’s refactor CreateTaskTest using Pages.

Let us first create a new Page:

php artisan dusk:page CreateTaskPage

The above command will create a new class in the Pages directory. Let us examine each method and modify it to better suit our Create Task test case:

public function url()
{
    return '/tasks/create';
}

url method defines the page url. Whenever this page is invoked, Dusk will navigate to this url.

public function assert(Browser $browser)
{
    $browser->assertPathIs($this->url());
}

assert defines the assertions for this page. Whenever CreateTaskPage is invoked, all the assertions defined in the assert method will execute.

In the above example, we are simply asserting that the active page’s url is appropriate.

public function elements()
{
    return [
        '@addTask' => '.btn-primary',
    ];
}

The elements method can have pre-defined selectors. We can define human readable names for selectors and reuse them for this page in different test cases. In the above example, I have defined a selector for the Add Task button.

Let us now modify the CreateTaskTest class and use the selector:

class CreateTaskTest extends DuskTestCase
{
    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_I_can_create_task_successfully()
    {
        $this->browse(function ($browser) {

            $user = factory(User::class)->create();

            $browser->loginAs($user)
                    ->visit(new CreateTaskPage)
                    ->type('title', 'My Task')
                    ->click('@addTask')
                    ->waitUntilMissing('@addTask')
                    ->assertPathIs('/tasks');
        });
    }
}

We modified our class to use the CreateTaskPage. Let us now re-run our test and see if everything works fine:

PHPUnit 5.7.13 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 2.76 seconds, Memory: 12.00MB

OK (1 test, 2 assertions)

All good!

You can even define custom methods for performing some reusable actions on a certain page. You can read more about it in the official documentation.

Conclusion

In this article, we introduced Laravel Dusk, a Laravel package which provides an alternative for end to end JavaScript testing. We have covered the configuration options necessary to get started, and the examples above should help you fill in the gaps and give an overview of some of the other configuration options available to you.

How do you use Dusk? Can you think of any advanced use cases for this library? What are they? Let us know in the comments!

Frequently Asked Questions (FAQs) about Laravel Dusk

How do I install Laravel Dusk for browser testing?

Laravel Dusk is a browser testing tool for Laravel applications. To install it, you need to have Composer installed on your system. Then, navigate to your Laravel application directory and run the command composer require --dev laravel/dusk. After the installation, run php artisan dusk:install to set up Dusk in your application. This will create a DuskTestCase.php file in your tests directory. This file will be used as the base for all your Dusk tests.

How do I write tests in Laravel Dusk?

Writing tests in Laravel Dusk is straightforward. First, you need to create a new test file in the tests/Browser directory. You can use the php artisan dusk:make TestName command to create a new test. In this file, you can use the $browser variable to interact with your application. For example, you can visit a page using $browser->visit('/url'), click links using $browser->clickLink('Link Text'), and assert that certain text is present using $browser->assertSee('Text').

How do I run Laravel Dusk tests?

To run your Laravel Dusk tests, navigate to your Laravel application directory in your terminal and run the command php artisan dusk. This will execute all your Dusk tests. If you want to run a specific test, you can specify the file name as an argument, like php artisan dusk tests/Browser/ExampleTest.php.

Can I use Laravel Dusk for testing JavaScript functionality?

Yes, Laravel Dusk is designed to test JavaScript-driven applications. It uses a real Chrome browser to perform tests, which means it can interact with your JavaScript just like a real user would. This includes clicking buttons that trigger JavaScript events, filling out forms that use JavaScript validation, and more.

How do I debug Laravel Dusk tests?

Laravel Dusk provides several methods for debugging tests. One of the most useful is the screenshot method, which takes a screenshot of the browser at the time the method is called. This can be useful for understanding what the browser is doing at a specific point in time. You can also use the dump method to print out the current HTML of the page.

Can I use Laravel Dusk with Continuous Integration (CI)?

Yes, Laravel Dusk can be used with most Continuous Integration services. The Dusk documentation provides instructions for setting up Dusk with several popular CI services, including Travis CI and CircleCI.

How do I handle authentication in Laravel Dusk tests?

Laravel Dusk provides a loginAs method that allows you to authenticate as a specific user. You can use this method to log in as a user before performing actions that require authentication.

Can I test file uploads with Laravel Dusk?

Yes, Laravel Dusk supports file uploads. You can use the attach method to simulate a file upload. This method takes two arguments: the name of the file input field, and the absolute path to the file you want to upload.

How do I handle database transactions in Laravel Dusk?

Unlike other Laravel testing tools, Dusk does not automatically rollback database transactions after each test. This is because Dusk uses a separate process to interact with your application, so it does not have access to the same database transaction. However, you can use the DatabaseMigrations trait in your tests to automatically migrate your database before each test and roll it back after.

Can I use Laravel Dusk to test APIs?

While it’s possible to use Laravel Dusk to test APIs by making requests and checking responses, it’s not the best tool for the job. Dusk is designed for browser testing, and there are other Laravel tools, like PHPUnit and Behat, that are better suited for API testing.