Cheatsheet: Laravel Dusk testing – login shortcut
Create default user and quickly use it to login. However, there is a
problem with this approach.
If you look into /test/Browser
folder, you will see another called: /Pages
.
Here’s what Laravel docs (5.6) have to say about this.
Scroll slightly below and you will see this Artisan command:
|
php artisan dusk:page Login |
When you run it, it will create class Login
inside of /Pages
.
This will be our default user login shortcut class.
But it is not ready yet.
We have to prep it a bit to serve its purpose.
We will add loginUser()
method to it.
This will be a method, that will be used behind scenes to log in defined there default user.
Before I go to this, a word of caution: do not name your login method login()
, but anything else – I used loginUser()
, as login()
name goes head on with another, Laravel native login()
method and is being overridden, giving you error like this, when you attempt to run your test:
|
Tests\Browser\YourTestClassTest::someTest Exception: User resolver has not been set. |
Login methods clash was not spotted by me. All credit due is here
Our Login class with changes is below.
A few tidbits in method comments to make things easier to understand – when you learn (or forgot something), nothing seems to be obvious.
And here is Login class with changes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
<?php namespace Tests\Browser\Pages; use Laravel\Dusk\Browser; use Laravel\Dusk\Page as BasePage; class Login extends BasePage { /** * Get the URL for the page. * * note: * This must match your login page address. * If your page address is (can be ralative): '/system/login', * then you visit() method in your test must match, e.g.: * $browser->visit('/system/login'), or you are going to get error give/take like this: * >>Failed asserting that '/system' matches PCRE pattern "/^\/system\/login$/u"<< * BTW: full URL will match relative, so this: '/system/login', will match * this: 'http://somedomain.com/system/login' * * @return string */ public function url() { return '/system/login'; } /** * Abstract the Login functionality * * note: * Do not forget to 'use' model on top, * or use inline namespace, like me below using: * \App\Models\System\SystemEmployee * * @param \Laravel\Dusk\Browser $browser * @return void */ public function loginUser(Browser $browser) { $browser->loginAs(\App\Models\System\SystemEmployee::where('email', 'system@gmail.com')->firstOrFail(), 'systems'); } /** * Assert that the browser is on the page. * * @param Browser $browser * @return void */ public function assert(Browser $browser) { $browser->assertPathIs($this->url()); } /** * Get the element shortcuts for the page. * * @return array */ public function elements() { return [ '@element' => '#selector', ]; } } |
Now, how to use it.
|
$this->browse(function (Browser $browser) { $browser ->visit('https://prado247.net/system/login') ->on(new \Tests\Browser\Pages\Login()) ->loginUser(); }); |
There is a PROBLEM with this approach.
It is good, if you use Laravel out of the box single level authorization, which is good for blog, when supported by user roles.
Simply you have single single admin and allow access rights based on roles.
In most cases I worked on, I needed 3 separate site areas, with two, that needed authentication.
Take ecommerce: backend admin / customer admin.
Take any company website, that allows users to have accounts: backend admin / customer admin.
You could try roles, but good luck with that.
You need multi-level authentication capacity
Note: what you find at the end of the link listed above, was written for Laravel 5.2 and may need some touchups.
Laravel does not have multi-level auth out of the box (as of 5.6)
So, if you are using Laravel provided single level authentication, you are in luck.
You can use this method described above.
You can also use methods stored in here:
|
vendor\laravel\dusk\src\Concerns\InteractsWithAuthentication.php |
Note:
If you are trying to use these methods and getting error to the tune of this:
|
Tests\Browser\YourTestClassTest::someTest Exception: User resolver has not been set. |
… you have to override protected user()
method from vendor\laravel\dusk\src\TestCase.php
in:
- your
TestClass
that extends tests\DuskTestCase
, or
- in
tests\DuskTestCase.php
itself
Whichever serves you best.
Class tests\DuskTestCase
seems best, as all your TestClasses will extend it, so you do it once and use it from now on.
Now, as I mentioned these methods are rigged to serve Laravel built in authentication.
Just look into: \vendor\laravel\dusk\src\DuskServiceProvider.php
and you’ll see rigged routes, like:
|
Route::get('/_dusk/login/{userId}/{guard?}', [ //<=== here 'middleware' => 'web', 'uses' => 'Laravel\Dusk\Http\Controllers\UserController@login', ]); |
Then these routes work with hardcoded methods in vendor\laravel\dusk\src\Concerns\InteractsWithAuthentication.php
:
|
public function loginAs($userId, $guard = null) { $userId = method_exists($userId, 'getKey') ? $userId->getKey() : $userId; return $this->visit(rtrim('/_dusk/login/'.$userId.'/'.$guard, '/')); //<=== here } |
You could set this for your needs in DuskServiceProvider
, but that would also require rewriting methods in vendor\laravel\dusk\src\Concerns\InteractsWithAuthentication.php
, which is nothing short of messing with core files, that can be replaced in next framework update.
Not really recommended.
You could overload these methods, but then, why not writing your own 😉
Better use other means of checking login.
They are all described in Tests
section.