使用PHPUnit和Mockery的Laravel测试-设置控制器测试的依赖项
在我终于通过了愚蠢的简单测试之后,我有一种感觉,我做得不对 我有一个SessionController,负责显示登录页面并让用户登录 我决定不使用facades,这样我就不必扩展Laravel的测试用例,也不必在我的单元测试中受到性能的影响。因此,我通过控制器注入了所有依赖项,如下所示- 会话控制器-构造函数使用PHPUnit和Mockery的Laravel测试-设置控制器测试的依赖项,php,unit-testing,laravel,dependency-injection,laravel-4,Php,Unit Testing,Laravel,Dependency Injection,Laravel 4,在我终于通过了愚蠢的简单测试之后,我有一种感觉,我做得不对 我有一个SessionController,负责显示登录页面并让用户登录 我决定不使用facades,这样我就不必扩展Laravel的测试用例,也不必在我的单元测试中受到性能的影响。因此,我通过控制器注入了所有依赖项,如下所示- 会话控制器-构造函数 public function __construct(UserRepositoryInterface $user, AuthMa
public function __construct(UserRepositoryInterface $user,
AuthManager $auth,
Redirector $redirect,
Environment $view )
{
$this->user = $user;
$this->auth = $auth;
$this->redirect = $redirect;
$this->view = $view;
}
我已经完成了必要的变量声明并使用了名称空间,我不打算在这里将其作为不必要的内容
create方法检测用户是否被授权,如果他们被授权,那么我将他们重定向到主页,否则他们将显示在登录表单中
会话控制器-创建
public function create()
{
if ($this->auth->user()) return $this->redirect->to('/');
return $this->view->make('sessions.login');
}
现在,对于测试,我是全新的,所以请容忍我
会话控制器测试
class SessionsControllerTest extends PHPUnit_Framework_TestCase {
public function tearDown()
{
Mockery::close();
}
public function test_logged_in_user_cannot_see_login_page()
{
# Arrange (Create mocked versions of dependencies)
$user = Mockery::mock('Glenn\Repositories\User\UserRepositoryInterface');
$authorizedUser = Mockery::mock('Illuminate\Auth\AuthManager');
$authorizedUser->shouldReceive('user')->once()->andReturn(true);
$redirect = Mockery::mock('Illuminate\Routing\Redirector');
$redirect->shouldReceive('to')->once()->andReturn('redirected to home');
$view = Mockery::mock('Illuminate\View\Environment');
# Act (Attempt to go to login page)
$session = new SessionsController($user, $authorizedUser, $redirect, $view);
$result = $session->create();
# Assert (Return to home page)
}
}
这一切都通过了,但我不想为我在SessionControllerTest中编写的每个测试声明所有这些模拟依赖项。有没有办法在构造函数中一次性声明这些模拟依赖项?然后通过这些变量调用它们进行模拟 是的,您可以使用“助手”。将模拟依赖项的创建移动到另一个函数中,然后在需要时调用该函数。查看本演示中的第52张幻灯片:(我们可以查看整个内容,但示例在第52张幻灯片上)
编辑:设置方式更好,我在考虑一些在所有测试中都不需要的东西,但我认为对于您在设置中描述的内容,这样做会更好。您可以使用
设置方法声明整个测试类的全局依赖项。它类似于您当前使用的tearDown
方法:
public function setUp()
{
// This method will automatically be called prior to any of your test cases
parent::setUp();
$this->userMock = Mockery::mock('Glenn\Repositories\User\UserRepositoryInterface');
}
但是,如果模拟的设置在不同的测试之间不同,那么这将不起作用。在这种情况下,可以使用助手方法:
protected function getAuthMock($isLoggedIn = false)
{
$authorizedUser = Mockery::mock('Illuminate\Auth\AuthManager');
$authorizedUser->shouldReceive('user')->once()->andReturn($isLoggedIn);
}
然后,当您需要auth mock时,只需调用getAuthMock
。这可以大大简化测试
但是
我认为你没有正确测试你的控制器。您不应该自己实例化控制器对象,而是应该使用Laravel的TestCase
类中存在的call
方法。请尝试查看Jeffrey Way关于测试Laravel控制器的内容。我认为您希望在测试中按照这一思路做更多的工作:
class SessionsControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
}
public function tearDown()
{
Mockery::close();
}
public function test_logged_in_user_cannot_see_login_page()
{
// This will bind any instances of the Auth manager during
// the next request to the mock object returned from the
// function below
App::instance('Illuminate\Auth\Manager', $this->getAuthMock(true));
// Act
$this->call('/your/route/to/controller/method', 'GET');
// Assert
$this->assertRedirectedTo('/');
}
protected function getAuthMock($isLoggedIn)
{
$authMock = Mockery::mock('Illuminate\Auth\Manager');
$authMock->shouldReceive('user')->once()->andReturn($isLoggedIn);
return $authMock;
}
}
谢谢@watcher!谢谢你的详尽回答谢谢你的推荐@Jessica,我很感激。