Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Failing unit testing of controller to check successful login in laravel 4.2

I am testing whether a successful log in happens or not. For that I am checking,

  • If successfully logged in
  • Application should redirect to dashboard

For that my controller looks like this

public function loginPost(){

    if (Auth::attempt(array(
        'email'     => Input::get('email'),
        'password'  => Input::get('password')
    ))){
        return Redirect::intended(route('dashboard'));
    }

    return Redirect::route('login')             
                        ->withInput()
                        ->with('errorMessage', 'Failed');
}

And my test looks like this

public function testLoginSuccess(){
     $input = [
         'email'                 => '[email protected]',
         'password'              => 'computer'
     ];

     Input::replace($input);

     Auth::shouldReceive('attempt')
           ->with($input)
           ->once()
           ->andReturn(true);

     $this->call('POST', 'login', $input);

     $this->assertRedirectedToRoute('dashboard');
 }

Although this works in browser. But while testing, it fails with the message:

BadMethodCallException: Method Mockery_0_Illuminate_Auth_AuthManager::check() does not exist on this mock object

like image 339
Nirmalz Thapaz Avatar asked Mar 16 '15 06:03

Nirmalz Thapaz


2 Answers

You didn't show the definition of your route, but I'm assuming your login route is protected by the guest before filter. This filter uses Auth::check() before dispatching to the route.

In your test, when you call Auth::shouldReceive(), that makes the Auth facade point to a mocked instance. Since you did not define an expectation on the mocked instance for the check() method, you're getting an error.

The easiest solution would be to go ahead and mock the Auth::check() method as well, and have it return false (to emulate visiting the route while not logged in).

public function testLoginSuccess() {
    $input = [
        'email' => '[email protected]',
        'password' => 'computer'
    ];

    Input::replace($input);

    // Tell Auth we're not logged in.
    Auth::shouldReceive('check')
        ->once()
        ->andReturn(false);

    Auth::shouldReceive('attempt')
        ->with($input)
        ->once()
        ->andReturn(true);

    $this->call('POST', 'login', $input);

    $this->assertRedirectedToRoute('dashboard');
}

You could also write a second test with Auth::check() mocked to return true in order to test what happens when you visit your login route while already logged in.

like image 92
patricus Avatar answered Oct 07 '22 00:10

patricus


In my opinion, this is a functional test not a unit test. There is absolutely no need for you keep mocking classes. You need to write a functional test for this.

If i had to do it, i would do something like this:

  • Send post request to login route with username, password, csrf_token etc.
  • Assert that page is redirected to a link suppose: http://localhost/home

So your test code in Laravel 4.2 will be something like this:

    // Update the following code accordingly for your app. 

    $credentials = array(
        'email'      => '[email protected]',
        'password'   => 'johndoe',
        'csrf_token' => csrf_token()
    );

    $this->withInput( $credentials )
        ->requestAction('POST', 'UserController@postLogin');

    $this->assertRedirection( URL::action('HomeController@getIndex') );
like image 28
Raza Mehdi Avatar answered Oct 07 '22 01:10

Raza Mehdi