How to repeat a dusk test ?

Published 8 months ago by LittlePadawan

Hello everyone,

I would like to know how to repeat a task to get all the combinasions.

<?php

namespace Tests\Browser;

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

class LoginTest extends DuskTestCase
{
    /**
     * @throws \Exception
     * @throws \Throwable
     */
    public function testConnection()
    {
        $this->browse(function (Browser $browser) {

            $browser
                    ->visit('/the-slug')
                    ->type('#email', '[email protected]')
                    ->type('#password', 'aaaaaa')
                    ->press('Connexion')
                    ;
        } );
    }
}

I don't want to create 4 different cases to tests everything. Is it possible to do it in one function and how?

topvillas

All the combinations of what?

LittlePadawan

I want to do this but in one function :

  • nothing => error
  • only email => error
  • only password => error
  • email + password => accepted
topvillas

I'm not a testing guru but can't you just make the browser call four times in the same function and make four assertions?

MikeHopley

Yeah, you can just continue chaining assertions:

$browser
    ->visit('/the-slug')
    ->press('Connexion')
    ->waitForText('Fill in the form, you fool')

    ->type('#email', '[email protected]')
    ->press('Connexion')
    ->waitForText('You must provide a password');

    // ...and so on
LittlePadawan

I can create many browsers but I will have to make all cases myself:

<?php

namespace Tests\Browser;

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

class LoginTest extends DuskTestCase
{
    /**
     * @throws \Exception
     * @throws \Throwable
     */
    public function testConnexionUser()
    {
        $this->browse(function ($first, $second) {
            $first
                    ->visit('/se-connecter')
                    ->press('Connexion');

            $second
                    ->visit('/se-connecter')
                    ->type('#email', '[email protected]')
                    ->press('Connexion');
                });
    }
}

With this method I still have to create all four cases. I don't know if it makes sense.

@MikeHopley Okay I'm going to try like this and come back here

MikeHopley

You don't want to create multiple browsers for this. Multiple browsers are for testing interactions across more than one browser.

For example, you could test a chat application where one user sends a message (in browser 1) and another user receives it (in browser 2).

The alternative to what I posted is breaking each case into separate tests, where each test is a method on your class. For more complicated forms, this is sometimes desirable, as it keeps the individual tests shorter and more focused. It helps you deal with failing tests more efficiently.

In this simple login case, I would just chain it inside one test (as shown), as I feel it's simpler.

LittlePadawan

@MikeHopley Okay I get it now why we need several browsers, thank you.

Here is what I did according what you said :

<?php

namespace Tests\Browser;

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

class LoginTest extends DuskTestCase
{
    /**
     * @throws \Exception
     * @throws \Throwable
     */
    public function testConnexionUser()
    {
    
        $this->browse(function (Browser $browser) {

            $browser
                    /**********************
                     * TEST ON LOGIN PAGE *
                     * ********************
                     */
                    ->visit('/se-connecter')

                    //make sure to see everything
                    ->assertSee('Connectez-vous')
                    ->assertSee('Email')
                    ->assertSee('Mot de passe')
                    ->assertSee('Mot de passe oubliƩ')
                    ->assertSee('Connexion')

                    //1ST CASE : no inputs
                    ->press('Connexion')

                    //2ND CASE : only email
                    ->type('#email','[email protected]')
                    ->press('Connexion')
                    ->clear('#email')

                    //3RD CASE : only password
                    ->type('#password','jeanjean')
                    ->press('Connexion')
                    ->clear('#password')

                    //4TH CASE : all inputs
                    ->type('#email','[email protected]')
                    ->type('#password','jeanjean')
                    ->press('Connexion')
                    ;
        } );
    }
}

It works. This is a simple form. I have a bigger form with many fields. Isn't there a way to automate it because I don't want to repeat every cases manually

MikeHopley

You haven't actually tested any behaviour there, because you have no assertions about what you are expecting to happen.

I used waitForText() in my example, which is not technically an assertion -- but it effectively functions as one. If the expected text does not appear within a (configurable) number of seconds, the test will fail.

Isn't there a way to automate it because I don't want to repeat every cases manually

Interesting question.

It would be nice to automate such things, but how? How could Laravel or Dusk possibly know what you intend?

You could potentially test all combinations of all fields, by using a model factory to provide dummy data, and writing some logic to work out all the combinations. But how would you determine success or failure?

There are ways to save time in Dusk. For example, you can create page objects that allow you to "bundle" multiple actions into a single, reusable method.

LittlePadawan

Okay !!! I was only testing if the connection was established or not. That's what I wanted to know.

I was thinking about creating an array in which I can insert all the fields and with a for loop I can access to each one of them. I'm going to try this and keep you in touch.

LittlePadawan

I don't know why this doesn't work :

<?php

namespace Tests\Browser;

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

class LoginTestOptimized extends DuskTestCase
{
    public function testConnectionOptimized()
    {
        $this->browse( /**
         * @param Browser $browser
         */
            function (Browser $browser)
        {
            //tableau dans un tableau : [[x],[y],[x,y,z]]
            //field => value
            $array = [
                ['#email' => '' ],
                ['#password' => '' ],
                [
                    '#email'    => '[email protected]', 
                    '#password' => 'jeanjean', 
                    '__clear__' => false
                ],
            ];

            $browser->visit('/se-connecter');

            foreach ($array as $line)
            {
                foreach ($line as $field => $value) {
                    $browser->type($field, $value);
                }

                $browser->press('Connexion')
                        ->clear($field, $value);
            }
        });
    }
}

Any idea guys?

MikeHopley

Um, looks like your last line isn't going to work, since $field and $value are not available outside the inner foreach loop.

And even if they were available, you'd only clear the last field, not all fields.

Also I don't understand what '__clear__' => false means.

Please sign in or create an account to participate in this conversation.