Laravel 5隔离控制器测试

Den*_*man 10 unit-testing laravel laravel-5

我正在尝试在Laravel 5中对我的控制器进行单元测试,但是有严重的问题缠绕在它周围.如果我真的想要进行隔离单元测试,似乎我必须交换依赖注入等效的大型短手函数和静态类.

首先,我在文档中看到的"单元测试",对我来说不是单元测试.它似乎更像是功能测试.我无法测试隔离的控制器功能,因为我将不得不通过整个框架,如果我有任何代码与我的数据库交互,将需要实际播种我的数据库.

所以,反过来,我想测试我的控制器隔离框架.然而,事实证明这很困难.

让我们看看这个示例函数(为了这个问题我已经保留了这个函数的一些部分):

public function postLogin(\Illuminate\Http\Request $request)
{
    $this->validate($request, [
        'email' => 'required|email', 'password' => 'required',
    ]);

    $credentials = $request->only('email', 'password');

    if (Auth::attempt($credentials, $request->has('remember')))
    {
        return redirect()->intended($this->redirectPath());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,问题出现在最后几行.当然,我可以模拟发送给函数的Request实例,这没问题.但是我如何模拟Auth类或重定向函数?我需要用依赖注入重写我的类/函数,如下所示:

private $auth;
private $redirector;

public function __construct(Guard $auth, \Illuminate\Routing\Redirector $redirector) 
{
    $this->auth = $auth;
    $this->redirector = $redirector;
}

public function postLogin(\Illuminate\Http\Request $request)
{
    $this->validate($request, [
        'email' => 'required|email', 'password' => 'required',
    ]);

    $credentials = $request->only('email', 'password');

    if ($this->auth->attempt($credentials, $request->has('remember')))
    {
        return $this->redirector->intended($this->redirectPath());
    }
}
Run Code Online (Sandbox Code Playgroud)

我最终得到了一个充满嘲讽的复杂单元测试:

public function testPostLoginWithCorrectCredentials()
{
    $guardMock = \Mockery::mock('\Illuminate\Contracts\Auth\Guard', function($mock){
        $mock->shouldReceive('attempt')->with(['email' => 'test', 'password' => 'test'], false)->andReturn(true);
    });

    $redirectorMock = \Mockery::mock('\Illuminate\Routing\Redirector', function($mock){
        $mock->shouldReceive('intended')->andReturn('/somePath');
    });

    $requestMock = \Mockery::mock('\Illuminate\Http\Request', function($mock){
        $mock->shouldReceive('only')->with('email', 'password')->andReturn(['email' => 'test', 'password' => 'test']);
        $mock->shouldReceive('has')->with('remember')->andReturn(false);
    });

    $object = new AuthController($guardMock, $redirectorMock);
    $this->assertEquals('/somePath', $object->postLogin($requestMock));
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我有更复杂的逻辑,例如,使用模型,我必须依赖注入,并在我的类中模拟它.

对我而言,似乎要么,Laravel没有提供我想要它做的事情,或者我的测试逻辑是有缺陷的.有没有什么方法可以测试我的控制器功能而不会失控测试功能和/或不得不依赖注入标准的Laravel类,无论如何我都可以在我的控制器中使用?

Lau*_*nce 10

你不应该试图单元测试控制器.它们旨在通过HTTP协议调用它们进行功能测试.这就是控制器的设计方式,Laravel提供了许多框架断言,您可以在测试中包含这些断言以确保它们按预期工作.

但是如果你想对控制器中包含的应用程序代码进行单元测试,那么你应该考虑使用命令.

使用命令可以将应用程序逻辑从控制器提取到类中.然后,您可以对类/命令进行单元测试,以确保获得预期的结果.

然后,您只需从控制器调用该命令即可.

事实上,Laravel文档告诉你:

我们可以将所有这些逻辑放在一个控制器方法中; 然而,这有几个缺点......单元测试命令更加困难,因为我们还必须生成存根HTTP请求并向应用程序发出完整请求以测试购买播客逻辑.