2014-04-29 90 views
7

终于让我愚蠢的简单测试通过后,我感觉我没有正确地做到这一点。Laravel使用PHPUnit和Mockery进行测试 - 在Controller上设置依赖关系test

我有一个SessionsController,这是负责显示登录页面和登录的用户。

我已经决定不使用外墙,这样我就不必延长Laravel的TestCase和采取的性能损失在我的单元测试中。因此,我通过控制器注入所有的依赖,就像这样 -

SessionsController - 构造

public function __construct(UserRepositoryInterface $user, 
          AuthManager $auth, 
          Redirector $redirect, 
          Environment $view) 
{ 
    $this->user = $user; 
    $this->auth = $auth; 
    $this->redirect = $redirect; 
    $this->view = $view; 
} 

我已经做了必要的变量声明和使用的命名空间,这我不会在这里包括它不必要的。

创建方法检测用户是否被授权,如果他们然后我将他们重定向到主页,否则会显示登录表单。

SessionsController - 创建

public function create() 
{ 
    if ($this->auth->user()) return $this->redirect->to('/'); 

    return $this->view->make('sessions.login'); 
} 

现在的测试,我是全新的,以它。因此,与我裸...

SessionsControllerTest

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

这一切都通过,但我不希望有声明所有为我在写SessionsControllerTest每个测试这些嘲笑依赖。有没有办法在构造函数中声明这些模仿的依赖关系?然后通过变量调用它们来模拟?

谢谢你的任何建议,并花时间来阅读我的问题。

回答

10

您可以使用setUp方法声明对整个测试类都是全局的任何依赖项。它类似于您目前使用的方法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'); 
} 

但是,不会,如果你的设置为测试之间的模拟不同的工作。当你需要在auth嘲笑,你可以叫getAuthMock

protected function getAuthMock($isLoggedIn = false) 
{ 
    $authorizedUser = Mockery::mock('Illuminate\Auth\AuthManager'); 
    $authorizedUser->shouldReceive('user')->once()->andReturn($isLoggedIn); 
} 

那么:对于这种情况,你可以使用一个辅助方法。这可以大大简化您的测试。

然而

我不认为你是正确的测试控制器。您不应该自己实例化控制器对象,而应该使用Laravel的TestCase类中存在的call方法。尝试检查出关于由Jeffrey Way测试Laravel控制器的this article。我认为你正在考虑在你的测试中做更多的事情:

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; 
    } 
} 
+0

谢谢@watcher!我很感谢你的全面回答 –

+1

np,很高兴提供帮助 –

2

是的,你可以使用“帮手”。将模拟的依赖关系创建为另一个函数,然后在需要时调用它。查看幻灯片52在这个演示文稿中:https://speakerdeck.com/jcarouth/guiding-object-oriented-design-with-tests-1(以及检查整个事情,但例子是在幻灯片52)

编辑:setUp的方式更好,我想的东西,你不需要在所有测试,但我认为你所描述的在setUp中做的更好。

+0

谢谢你参考@Jessica,我很感激。 –

相关问题