pratamatama's avatar

Why my test fails when I set $this->withoutExceptionHandling()?

I created a test for my app and find something weird with the authorization stuff which is failing when I enable the exception handling by command described by the title above.

This is the result when $this->withoutExceptionHandling() commented out:

   PASS  Tests\Unit\SubscriptionTest
  ✓ subscription table has expected columns        
  ✓ subscription table are seedable through factory
  ✓ subscription table are seedable through seeder 
  ✓ subscription belongs to an author
  ✓ subscription has many subscribers

   PASS  Tests\Feature\SubscriptionTest
  ✓ subscription should be indexable by guest user  
  ✓ subscription should be showable by guest user   
  ✓ subscription should be updated by internal user 
  ✓ subscription should not be created by guest user
  ✓ subscription should not be updated by guest user
  ✓ subscription should not be updated by non internal user
  ✓ subscription should not be deleted by guest user       

  Tests:  12 passed
  Time:   17.91s

And this is when $this->withoutExceptionHandling() uncommented:

   PASS  Tests\Unit\SubscriptionTest
  ✓ subscription table has expected columns        
  ✓ subscription table are seedable through factory
  ✓ subscription table are seedable through seeder
  ✓ subscription belongs to an author
  ✓ subscription has many subscribers

   FAIL  Tests\Feature\SubscriptionTest
  ✓ subscription should be indexable by guest user 
  ✓ subscription should be showable by guest user  
  ✓ subscription should be updated by internal user
  ⨯ subscription should not be created by guest user
  ⨯ subscription should not be updated by guest user
  ⨯ subscription should not be updated by non internal user
  ⨯ subscription should not be deleted by guest user

  ---

  • Tests\Feature\SubscriptionTest > subscription should not be created by guest user
   Illuminate\Auth\AuthenticationException 

  Unauthenticated.

  at C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:82 
     78▕      * @throws \Illuminate\Auth\AuthenticationException
     79▕      */
     80▕     protected function unauthenticated($request, array $guards)
     81▕     {
  ➜  82▕         throw new AuthenticationException(
     83▕             'Unauthenticated.', $guards, $this->redirectTo($request)
     84▕         );
     85▕     }
     86▕

  1   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:68
      Illuminate\Auth\Middleware\Authenticate::unauthenticated(Object(Illuminate\Http\Request))

  2   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:42
      Illuminate\Auth\Middleware\Authenticate::authenticate(Object(Illuminate\Http\Request))

  • Tests\Feature\SubscriptionTest > subscription should not be updated by guest user
   Illuminate\Auth\AuthenticationException 

  Unauthenticated.

  at C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:82
     78▕      * @throws \Illuminate\Auth\AuthenticationException
     79▕      */
     80▕     protected function unauthenticated($request, array $guards)
     81▕     {
  ➜  82▕         throw new AuthenticationException(
     83▕             'Unauthenticated.', $guards, $this->redirectTo($request)
     84▕         );
     85▕     }
     86▕

  1   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:68
      Illuminate\Auth\Middleware\Authenticate::unauthenticated(Object(Illuminate\Http\Request))

  2   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:42
      Illuminate\Auth\Middleware\Authenticate::authenticate(Object(Illuminate\Http\Request))

  • Tests\Feature\SubscriptionTest > subscription should not be updated by non internal user
   Illuminate\Auth\Access\AuthorizationException 

  This action is unauthorized.

  at C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Access\Response.php:119
    115▕      */
    116▕     public function authorize()
    117▕     {
    118▕         if ($this->denied()) {
  ➜ 119▕             throw (new AuthorizationException($this->message(), $this->code()))
    120▕                         ->setResponse($this);
    121▕         }
    122▕
    123▕         return $this;

  1   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Access\Gate.php:323
      Illuminate\Auth\Access\Response::authorize()

  2   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authorize.php:43
      Illuminate\Auth\Access\Gate::authorize("update")

  • Tests\Feature\SubscriptionTest > subscription should not be deleted by guest user
   Illuminate\Auth\AuthenticationException 

  Unauthenticated.

  at C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:82
     78▕      * @throws \Illuminate\Auth\AuthenticationException
     79▕      */
     80▕     protected function unauthenticated($request, array $guards)
     81▕     {
  ➜  82▕         throw new AuthenticationException(
     83▕             'Unauthenticated.', $guards, $this->redirectTo($request)
     84▕         );
     85▕     }
     86▕

  1   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:68
      Illuminate\Auth\Middleware\Authenticate::unauthenticated(Object(Illuminate\Http\Request))

  2   C:\Users\Fukka\Documents\Laravel\prismahr\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:42
      Illuminate\Auth\Middleware\Authenticate::authenticate(Object(Illuminate\Http\Request))


  Tests:  4 failed, 8 passed
  Time:   17.83s

And here is my tests if you guys curious:

<?php

namespace Tests\Feature;

use App\Models\Subscription;
use App\Models\User;
use Database\Seeders\RolesAndPermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use Spatie\Permission\PermissionRegistrar;

class SubscriptionTest extends TestCase
{
    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();
        $this->withoutExceptionHandling();
        $this->app->make(PermissionRegistrar::class)->registerPermissions();
        $this->seed(RolesAndPermissionSeeder::class);
    }

    public function testSubscriptionShouldBeIndexableByGuestUser()
    {
        $subscriptions = Subscription::factory(3)->create()->toArray();
        $this->getJson(route('subscriptions.index'))
            ->assertOk()
            ->assertExactJson($subscriptions);
    }

    public function testSubscriptionShouldBeShowableByGuestUser()
    {
        $subscription = Subscription::factory()->create();
        $this->getJson(route('subscriptions.show', $subscription))
            ->assertOk()
            ->assertExactJson($subscription->toArray());
    }

    public function testSubscriptionShouldBeUpdatedByInternalUser()
    {
        $usr = User::factory()->create()->assignRole('internal');
        $old = Subscription::factory()->create(['user_id' => $usr->id]);
        $new = Subscription::factory()->make(['user_id' => $usr->id])->toArray();

        $this->actingAs($usr, 'api')
            ->putJson(route('subscriptions.update', $old), $new)
            ->assertOk()
            ->assertJsonFragment($new);
    }

    public function testSubscriptionShouldNotBeCreatedByGuestUser()
    {
        $subscription = Subscription::factory()->make()->toArray();
        $this->postJson(route('subscriptions.store', $subscription))
            ->assertStatus(401)
            ->assertJsonMissingExact($subscription)
            ->assertExactJson(['message' => 'Unauthenticated.']);
    }

    public function testSubscriptionShouldNotBeUpdatedByGuestUser()
    {
        $old = Subscription::factory()->create();
        $new = Subscription::factory()->make()->toArray();

        $this->putJson(route('subscriptions.update', $old), $new)
            ->assertStatus(401)
            ->assertExactJson(['message' => 'Unauthenticated.']);
    }

    public function testSubscriptionShouldNotBeUpdatedByNonInternalUser()
    {
        $usr = User::factory()->create()->assignRole('customer');
        $old = Subscription::factory()->create(['user_id' => $usr->id]);
        $new = Subscription::factory()->make(['user_id' => $usr->id])->toArray();

        $this->actingAs($usr, 'api')
            ->putJson(route('subscriptions.update', $old), $new)
            ->assertForbidden()
            ->assertJsonMissingExact($new);
    }

    public function testSubscriptionShouldNotBeDeletedByGuestUser()
    {
        $subscription = Subscription::factory()->create();
        $this->deleteJson(route('subscriptions.destroy', $subscription))
            ->assertStatus(401)
            ->assertJsonMissingExact($subscription->toArray())
            ->assertExactJson(['message' => 'Unauthenticated.']);
    }
}
0 likes
5 replies
tykus's avatar
tykus
Best Answer
Level 104

Your assertion is a 401 response code; but you cannot get this response code unless you allow Laravel to handle the AuthenticationException (it does not handle the exception if you turn off exception handling). If you dive into the framework here, you will see that this is where the 401 status code comes from.

pratamatama's avatar

So, what should I use for the assertion? I tried assertUnauthorized and assertForbidden, they doesn't work

tykus's avatar

You must allow the framework to handle the Exception if you want to use assertions that depend on the response status code; in that case do not disable exception handling. Why is it necessary for you to turn off exception handling for feature tests?

1 like
pratamatama's avatar

Just curious because my test are failing when I do so.. So, it is not a good practice to disable exception handling on feature test?

tykus's avatar

In general, I use withoutExceptionHandling whenever I am trying to diagnose an unexpected test failure - exception handling likely is masking the underlying cause, and disabling exception handling exposes the stacktrace.

1 like

Please or to participate in this conversation.