控制器单元测试失败,无法在 laravel 4.2 中检查登录是否成功


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

我正在测试是否成功登录。为此,我正在检查,

  • 如果已成功登录
  • 应用程序应重定向到仪表板

为此,我的controller看起来像这样

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');
}

我的test看起来像这样

public function testLoginSuccess(){
     $input = [
         'email'                 => 'xyz@gmail.com',
         'password'              => 'computer'
     ];
     Input::replace($input);
     Auth::shouldReceive('attempt')
           ->with($input)
           ->once()
           ->andReturn(true);
     $this->call('POST', 'login', $input);
     $this->assertRedirectedToRoute('dashboard');
 }

虽然这在浏览器中有效。但在测试时,它失败并显示以下消息:

BadMethodCallException:方法 Mockery_0_Illuminate_Auth_AuthManager::check() 在此模拟对象上不存在

您没有显示路由的定义,但我假设您的login路由受guest before过滤器的保护。此筛选器在调度到路由之前使用Auth::check()

在您的测试中,当您调用 Auth::shouldReceive() 时,会使Auth外观指向模拟实例。由于您没有为 check() 方法定义对模拟实例的期望,因此您会收到错误。

最简单的解决方案是继续模拟Auth::check()方法,并让它返回false(以模拟在未登录时访问路由)。

public function testLoginSuccess() {
    $input = [
        'email' => 'xyz@gmail.com',
        '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');
}

您还可以编写第二个测试,模拟Auth::check()以返回true,以测试当您在已登录的情况下访问登录路由时会发生什么。

在我看来,这是一个功能测试,而不是单元测试。你绝对没有必要继续嘲笑课程。您需要为此编写一个功能测试。

如果我必须这样做,我会做这样的事情:

  • 使用用户名、密码、csrf_token等向登录路由发送帖子请求。
  • 断言该页面被重定向到链接,假设:http://localhost/home

所以你在Laravel 4.2中的测试代码将是这样的:

    // Update the following code accordingly for your app. 
    $credentials = array(
        'email'      => 'john@example.com',
        'password'   => 'johndoe',
        'csrf_token' => csrf_token()
    );
    $this->withInput( $credentials )
        ->requestAction('POST', 'UserController@postLogin');
    $this->assertRedirection( URL::action('HomeController@getIndex') );

也许,这个堆栈溢出线程可能会有所帮助。


顺便说一句:我同意@Raza Mehdi的观点。

我认为从高层次测试登录功能,测试我们的实际代码,而不是模拟Auth对象(我们不拥有的类型)。

  • 调用登录路由,传递用户凭据。
  • 断言状态为"正常"。
  • 断言用户被重定向到仪表板。

在代码中:

 $input = [...];
 $this->call('POST', 'login', $input);
 $this->assertResponseStatus(200);
 $this->assertRedirectedToRoute('dashboard');