Agoi's avatar
Level 1

Test Wrong Password throws an Exception During Auth Test

Hi everyone,

I have a code like below

        $user = \App\User::where('email', trim($this->email))->firstOrFail();

        //compare password
        if (! Hash::check(trim($this->password), $user->password) ) {
            throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
        }

        $user->update([
            'auth_token' => custom_unique('AUTH_TOKEN'),
        ]);

        Auth::login($user, true);

        return $user;

which throws an exception when the password is not valid and I try to test for this scenario with below code but tests fail:

    public function testWrongCredentialThrowsException()
    {
        $this->withoutExceptionHandling();
        
        $attributes = [
            'email' => ' [email protected] ',
            'password' => 'abc',
        ];

        $user = factory(\App\User::class)->create($attributes);

        $this->post('/api/auth/store', $attributes)->assertSuccessful();

        $user = \App\User::where('email', '=', trim($attributes['email']))->first();

        if (!Hash::check('abcd', $user->password)) {
            $this->expectException(ModelNotFoundException::class);
        }
    }

Please can you help me check what I am doing wrong

0 likes
8 replies
Sti3bas's avatar
Sti3bas
Best Answer
Level 53

@agoi expectException should be called before exception is thrown. Move it to the top of the test:

public function testWrongCredentialThrowsException()
{
   $this->withoutExceptionHandling();
   $this->expectException(ModelNotFoundException::class);
        
   $user = factory(\App\User::class)->create();

   $this->post('/api/auth/store', [
      'email' => $user->email,
      'password' => 'wrongpassword',
   ]);
}
Agoi's avatar
Level 1

Hi @sti3bas. After changing to your implementation using the exact code you typed above, I still have the error

There was 1 failure:

1) Tests\Feature\AuthenticationTest::testWrongCredentialThrowsException
Failed asserting that exception of type "Illuminate\Database\Eloquent\ModelNotFoundException" is thrown.

/Users/agoi/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:206
/Users/agoi/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:162
Agoi's avatar
Level 1

Yes, see below:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests\AuthFormRequest;
use App\Http\Resources\AuthUserResource;
use Symfony\Component\HttpFoundation\Response;

class AuthController extends BaseApiController
{
    /**
     * Handle the process of authenticating user
     *
     * @return
     */
    public function store(AuthFormRequest $request)
    {
        try {
                return $this->customResponse(new AuthUserResource($request->handle()), Response::HTTP_OK, 'Authenticated was successful');
        } catch (\Exception $e) {
            return $this->customResponse('', Response::HTTP_UNPROCESSABLE_ENTITY, 'Credentials not valid');
        }
    }
}
Agoi's avatar
Level 1

Or is this failing because I later caught the exception within my controller?

Agoi's avatar
Level 1

AuthFormRequest Below:

<?php

namespace App\Http\Requests;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class AuthFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'email' => 'required|email',
            'password' => 'required',
        ];
    }

    /**
     * Handle the authencating new user
     * @return
     */
    public function handle()
    {
        $user = \App\User::where('email', trim($this->email))->firstOrFail();

        //compare password
        if (! Hash::check(trim($this->password), $user->password) ) {
            throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
        }

        $user->update([
            'auth_token' => custom_unique('AUTH_TOKEN'),
        ]);

        Auth::login($user, true);

        return $user;
    }

}
Agoi's avatar
Level 1

In the end, I wasn't really throwing an exception as I later caught the exception.

Thanks @sti3bas

Sti3bas's avatar

@agoi well, this is a feature test, so I would recommend you to assert the response instead of checking for exceptions (implementation details):

public function testWrongCredentialThrowsException()
{
   $this->withoutExceptionHandling();
        
   $user = factory(\App\User::class)->create();

   $response = $this->post('/api/auth/store', [
      'email' => $user->email,
      'password' => 'wrongpassword',
   ])->assertStatus(404);

   // other assertions if needed
}

If you really need to check that it throws ModelNotFoundException exception, create a unit test for AuthFormRequest class.

1 like

Please or to participate in this conversation.