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.
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:
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:
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:
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:
- Enter the title
- Click the Add Task button
- Wait for 5 seconds
- Assert that we are redirected to the
tasks
page
Let us now run our Dusk tests and see the result:
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:
We will write a test case to test the following scenario:
- Log in
- See Support Email
- Click on Support Email
- 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.
Viraj Khatavkar is a software developer, writer, speaker, and entrepreneur from Mumbai, India. He likes to challenge a complex set of problems with PHP. He’s fond of servers, swims and sometimes blogs