Php 什么';在Laravel中测试控制器的正确方法是什么?
我正在重写现有的Laravel4应用程序,以确保有足够的测试。长话短说,我已经用TDD方法重写了我的Php 什么';在Laravel中测试控制器的正确方法是什么?,php,unit-testing,laravel,mockery,Php,Unit Testing,Laravel,Mockery,我正在重写现有的Laravel4应用程序,以确保有足够的测试。长话短说,我已经用TDD方法重写了我的AccountController类,我有点头疼 考虑以下呈现包含用户列表的页面的方法: public function getIndex() { // build the view // return \View::make('account.list-users') ->with('users', \Sentry::getUserProvider(
AccountController
类,我有点头疼
考虑以下呈现包含用户列表的页面的方法:
public function getIndex()
{
// build the view
//
return \View::make('account.list-users')
->with('users', \Sentry::getUserProvider()->findAll());
}
我正在使用Smarty呈现我的视图,并使用Sentry进行身份验证
现在,我想写一些如下的测试:
public function test_getIndex()
{
// arrange
//
// set up some mocks here...
// act
//
$response = $this->client->request("GET", "/list-users");
// assert
//
// test for <table class="table">
$this->assertFalse($response->filter("table.table")==null, "table not found");
// test for some <a> tags for the "update" buttons
$element = $response->filter("td a")->first()->extract(array("href", "class", "_text"));
$this->assertTrue(strstr($element[0][0],"/my-update-url")!="");
$this->assertTrue(strstr($element[0][1],"btn btn-xs btn-success")!="");
$this->assertTrue(strstr($element[0][2],"Active")!="");
// test for some other markup...
}
如果我采用这种方法,测试会简单得多,我只测试实际在控制器方法中的代码,但我并没有真正测试调用该路由所涉及的所有内容(这可能是件好事,也可能不是,我不确定),我担心我得不到足够的覆盖
那么,最好的方法是什么:(A)、(B)还是别的
编辑
对于我的控制器方法的测试,我有相当多的困惑,下面@TheShift Exchange的回答和评论更清楚地说明了这一点。作为编辑,我将在这里尝试解决这个问题,因为它给了我更多的空间来讨论这个问题
考虑下面答案中给出的第二个例子:
public function testMethod()
{
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
如果我运行这个测试,它会工作,但它会访问数据库,我试图通过模拟一些东西来避免访问数据库
所以,我可以稍微扩展一下这个测试:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
这个测试将不起作用,因为除了controller方法所需的mock之外,我还需要在我的View composer中对代码进行mock。此代码包括,$currentUser=\Sentry::getUser()
(用户名显示在我的应用程序页面的右上角)
因此,代码实际上变成:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
// plus a mock of ThisClass
// and a mock of ThatClass
// and a mock of SomeOtherClass
// etc.
// etc.
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
它很快就失控了
这对我来说意味着我做错了什么,但我不确定是什么。我怀疑这个问题源于我不确定我到底在测试什么
因此,在所有这些之后,问题变成了:
当我测试控制器的方法时,我真正想要测试的是什么
也许,与其通过
$this->call()
测试代码,我需要直接调用控制器方法?作为Laravel框架的一部分。这些帮助程序中包括视图测试帮助程序:
断言视图有一些数据
public function testMethod()
{
$this->call('GET', '/');
$this->assertViewHas('name');
$this->assertViewHas('age', $value);
}
这样您就可以执行如下操作:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn('foo');
$this->call('GET', '/');
$this->assertViewHas('users', 'foo');
}
是的,我知道这些断言,但我的问题更多的是关于测试的设置,而不是最后的断言。我不明白-你想测试什么?如果视图获得了用户,那么您还需要测试什么?似乎我已经用两行代码而不是40行代码测试了你的控制器功能?谢谢你的评论-你帮助我思考了更多的问题&我编辑了这个问题以(我希望)澄清它。我认为你试图在一个功能中测试太多了。如果控制器响应正确的路由并使用正确的数据调用正确的视图,那么控制器测试就是正确的。就这样。它不应该测试视图本身。同时,只需为视图生成器编写另一个单独的测试——它调用所有正确的变量。好的,我明白了。这是有道理的——我一直在嘲笑这些东西,因为我试图避免数据库访问(因为我认为这很重要)——这在这些测试中不重要吗?
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn('foo');
$this->call('GET', '/');
$this->assertViewHas('users', 'foo');
}