Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test an authorization redirect with Laravel?

Tags:

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.

like image 409
tread Avatar asked Aug 07 '16 08:08

tread


People also ask

How do I redirect in Laravel?

Redirecting to Controller Actions Not only named route but we can also redirect to controller actions. We need to simply pass the controller and name of the action to the action method as shown in the following example. If you want to pass a parameter, you can pass it as the second argument of the action method.

How would you redirect back to a controller action?

Redirect to a Controller Action The last piece of the puzzle is that you can redirect to a specific Method of a specific Controller, no matter what its Route or URL is – use method action(): return redirect()->action('App\Http\Controllers\BooksController@index');


2 Answers

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.

like image 173
Mikhail Zhuravlev Avatar answered Oct 08 '22 04:10

Mikhail Zhuravlev


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.'

like image 27
David J Eddy Avatar answered Oct 08 '22 04:10

David J Eddy