Saturday, August 20, 2016

How to unit test an authorization redirect with Laravel?

Leave a Comment

I have manually tested the scenario I want:

Admin Users can go to the /codes section of the site. Normal users are redirected (302) back to the /dashboard and have a message Sorry you are not allowed there when they go to /qr.

Manual testing passes, yet laravel testing fails.

I am using laravel 5.1

Test for the admin user:

public function testAdminViewCodes()     {         //create an admin user         $user = factory(App\User::class, 'admin')->create();          $this->actingAs($user)             ->visit('/codes')             ->seePageIs('/codes')             ->see('Codes');     } 

Test for normal user:

    public function testNormalViewCodesFail()     {         //create a normal user         $normal_user = factory(App\User::class)->create();          //TODO: Fix this failing test FFS          $this->actingAs($normal_user)              ->visit('/qr')              ->seePageIs('/dashboard')              ->see('Sorry you are not allowed there'); } 

test results;

There was 1 failure:  1) AdminTest::testNormalViewQRCodesFail Did not land on expected page [http://localhost/dashboard].  Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'http://localhost/dashboard' +'http://localhost/codes' 

I think there may be an issue with the factories, seems to be always creating an admin user:

$factory->define(App\User::class, function (Faker\Generator $faker) {     return [         'email' => $faker->email,         'password' => bcrypt(str_random(10)),         'remember_token' => str_random(10),         'is_admin' => false,     ]; });  $factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) {     $user = $factory->raw(App\User::class);      return array_merge($user, ['is_admin' => true]); }); 

My apologies for how long this question is, but there is another pertanent issue. I am using middleware to test whether the user is admin:

<?php  namespace RMS\Http\Middleware;  use Closure;  class IsAdminMiddleware {     /**      * Handle an incoming request.      *      * @param  \Illuminate\Http\Request  $request      * @param  \Closure  $next      * @return mixed      */     public function handle($request, Closure $next)     {         if (app()->env === 'testing') {             return $next($request);         }          if (! $request->user()->isAdmin()) {           return redirect()->route('dashboard')               ->with('message', 'Sorry you are not allowed there');         }          return $next($request);     } } 

In Kernel.php:

protected $routeMiddleware = [         'auth' => \RMS\Http\Middleware\Authenticate::class,         'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,         'guest' => \RMS\Http\Middleware\RedirectIfAuthenticated::class,         'isadmin' => \RMS\Http\Middleware\IsAdminMiddleware::class,     ]; 

And applied to routes:

Route::group(['middleware' => ['auth', 'isadmin']], function() {     Route::resource('user', 'UserController'); }); 

Is Middleware ignored? I was sure not to add the use WithoutMiddleware; statement.

3 Answers

Answers 1

You have two options:

  • better create tests for user factory, like it can make user types you want
  • break with a debugger after user generation to inspect it manually

I recommend you to create tests, beacuse you have doubt now, and in future there is a chance of accidently breaking the factory code, since it's not so obvious.

Answers 2

As an aside: unit tests are not meant to be user experience tests. For that is would be acceptance or functional testing. One of the more popular tools for that is codeception. It combined with phantomjs or selenium can emulate a browser session and get full user experience rendering.

Per docs available at http://codeception.com/docs/01-Introduction docs:

Acceptance Tests : 'Acceptance tests can cover standard but complex scenarios from a user’s perspective. With acceptance tests you can be confident that users, following all defined scenarios, won’t get errors.'

Functional Tests : 'functional tests you emulate a web request ($_GET and $_POST variables) and send it into your application which returns HTML response. '

Unit Tests : 'Testing pieces of code before coupling them together is highly important as well. This way you can be sure that some deeply hidden feature still works, even if it was not covered by functional or acceptance tests. This also proves that you produced stable and testable code.'

Answers 3

I would suggest using Authenticate a User Instance Auth::login($user); for more details read here.

This method is valid for Laravel 5.x and up

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment