rhand's avatar
Level 6

Login Unit Test fails

Trying to test if login controller works well so if authentication takes place, if validation takes place. Currently have this PHP unit test that seems to fail on validation:

<?php

namespace Tests\Feature\Http\Controllers\Auth;

use App\Models\Auth\User;
use Database\Factories\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class LoginControllerTest extends TestCase
{
    /**
     * A basic login view display test.
     *
     * @link https://jasonmccreary.me/articles/start-testing-laravel/
     *
     * @return void
     */

    /** @test */
    public function login_displays_the_login_form()
    {
        $response = $this->get(route('login'));

        $response->assertStatus(200);
        $response->assertViewIs('auth.login');
    }

    /**
     * Test that user cannot view login form when authenticated.
     *
     * It’s enough that some user is authenticated. He doesn’t need to be in the DB. So a
     * simple ->make() and actingAs() will be enough in this case.
     *
     * @link https://medium.com/@DCzajkowski/testing-laravel-authentication-flow-573ea0a96318
     * @link https://laracasts.com/discuss/channels/code-review/please-help-call-to-undefined-function-factory
     *
     * In Laravel 8, the factory helper is no longer available. Your model class should use HasFactory
     */

    /** @test */
    public function test_user_cannot_view_a_login_form_when_authenticated()
    {
        // Create a single App\Models\User instance...
        // User::factory()->make();
        // Create three App\Models\User instances...
        // User::factory()->count(3)->create();
        $user = User::factory()->make();

        // act as logged in user set using the factory model
        $response = $this->actingAs($user)->get('/login');

        // make sure redirect is to dashboard
        $response->assertRedirect('/dashboard');
    }

    /**
     * Login Display Validation errors using post() and assertStatus, AssertSessionhasErrors.
     *
     * @link https://laracasts.com/discuss/channels/testing/session-is-missing-expected-key-errors-failed-asserting-that-false-is-true?page=1&replyId=713577
     */

    /** @test */
    public function login_displays_validation_errors()
    {
        // php artisan config:clear
        // Never run "php artisan config:cache" or anything that might make it run, in development.
        // .env.testing is a solid idea as well
        $response = $this->post('/login', []);

        $response->assertStatus(302);
        $response->assertSessionHasErrors('email');
    }
}

Error I am getting is

 FAIL  Tests\Feature\Http\Controllers\Auth\LoginControllerTest
  ✓ login displays the login form
  ✓ user cannot view a login form when authenticated
  ⨯ login displays validation errors

View has this

...
<form class="form-horizontal" role="form" method="POST" action="{{ route('login') }}">
    {{ csrf_field() }}

    <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">

        <label>email address</label>
        <input id="email" placeholder="Email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus>

        @if ($errors->has('email'))
            <span class="help-block">
                <strong>{{ $errors->first('email') }}</strong>
            </span>
        @endif
    </div>
...

Any ideas how I can validate for no input, wrong input properly?

0 likes
4 replies
Tray2's avatar
Tray2
Best Answer
Level 74

I suggest starting with adding the following line of code in the test that fails to get more information about why it fails.

$this->withoutExceptionHandling();

like so

/** @test */
    public function login_displays_validation_errors()
    {
		$this->withoutExceptionHandling()
        $response = $this->post('/login', []);

        $response->assertStatus(302);
        $response->assertSessionHasErrors('email');
    }

It will probably tell you more

1 like
rhand's avatar
Level 6

Just did. Good tip. Do get more details

 Tests\Feature\Http\Controllers\Auth\LoginControllerTest > login displays validation errors
   PHPUnit\Framework\ExceptionWrapper 

  CSRF token mismatch.

  at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:85
     81▕                 }
     82▕             });
     83▕         }
     84▕ 
  ➜  85▕         throw new TokenMismatchException('CSRF token mismatch.');
     86▕     }
     87▕ 
     88▕     /**
     89▕      * Determine if the HTTP request uses a ‘read’ verb.


  Tests:  1 failed, 4 passed
  Time:   0.42s

Then I cleared config cache and did things again

php artisan config:clear

and ran the test again

php artisan test 

   PASS  Tests\Unit\JsonObjectTest
  ✓ get exists property
  ✓ get undefined property

   FAIL  Tests\Feature\Http\Controllers\Auth\LoginControllerTest
  ✓ login displays the login form
  ✓ user cannot view a login form when authenticated
  ⨯ login displays validation errors

  ---

  • Tests\Feature\Http\Controllers\Auth\LoginControllerTest > login displays validation errors
   PHPUnit\Framework\ExceptionWrapper 

  The email field is required. (and 1 more error)

  at vendor/laravel/framework/src/Illuminate/Support/helpers.php:323
    319▕     function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
    320▕     {
    321▕         if ($condition) {
    322▕             if (is_string($exception) && class_exists($exception)) {
  ➜ 323▕                 $exception = new $exception(...$parameters);
    324▕             }
    325▕ 
    326▕             throw is_string($exception) ? new RuntimeException($exception) : $exception;
    327▕         }


  Tests:  1 failed, 4 passed
  Time:   0.46s

and then without $this->withoutExceptionHandling(); and all good.

Tray2's avatar

@rhand I always add that line to every feature test I write, and then remove it when finished.

1 like
rhand's avatar
Level 6

@Tray2 Yeah, it helps to see what is going on in the background. Thanks a lot for pointing this out Tray2.

Please or to participate in this conversation.