Laravel 5.1 中的模拟请求用于(实际)单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31822541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mocking Request in Laravel 5.1 for (actual) Unit Test
提问by Christopher Francisco
First of all, I'm aware the docsstates:
首先,我知道文档指出:
Note: You should not mock the Request facade. Instead, pass the input you desire into the HTTP helper methods such as call and post when running your test.
注意:您不应该模拟 Request 门面。相反,在运行测试时将您想要的输入传递到 HTTP 帮助程序方法中,例如 call 和 post。
Butthose kind of tests are more like integration or functionalsince even though you're testing a controller(the SUT
), you're not decoupling it from it's dependencies (Request
and others, more on this later).
但是这些类型的测试更像是集成或功能性的,因为即使您正在测试控制器(the SUT
),您也不会将它与它的依赖项(Request
以及其他的,稍后详细介绍)分离。
So what I'm doing, in order to do the correct TDD
loop, is mocking the Repository
, Response
and Request
(which I have problems with).
因此,为了执行正确的TDD
循环,我正在做的是嘲笑Repository
,Response
和Request
(我遇到了问题)。
My test look like this:
我的测试看起来像这样:
public function test__it_shows_a_list_of_categories() {
$categories = [];
$this->repositoryMock->shouldReceive('getAll')
->withNoArgs()
->once()
->andReturn($categories);
Response::shouldReceive('view')
->once()
->with('categories.admin.index')
->andReturnSelf();
Response::shouldReceive('with')
->once()
->with('categories', $categories)
->andReturnSelf();
$this->sut->index();
// Assertions as mock expectations
}
This works perfectly fine, and they follow the Arrange, Act, Assertstyle.
这工作得非常好,并且它们遵循排列、执行、断言的风格。
The problem is with Request
, like the following:
问题在于Request
,如下所示:
public function test__it_stores_a_category() {
Redirect::shouldReceive('route')
->once()
->with('categories.admin.index')
->andReturnSelf();
Request::shouldReceive('only')
->once()
->with('name')
->andReturn(['name' => 'foo']);
$this->repositoryMock->shouldReceive('create')
->once()
->with(['name' => 'foo']);
// Laravel facades wont expose Mockery#getMock() so this is a hackz
// in order to pass mocked dependency to the controller's method
$this->sut->store(Request::getFacadeRoot());
// Assertions as mock expectations
}
As you can see I have mocked Request::only('name')
call. But when I run $ phpunit
I get the following error:
正如你所看到的,我模拟了Request::only('name')
通话。但是当我运行时,$ phpunit
我收到以下错误:
BadMethodCallException: Method Mockery_3_Illuminate_Http_Request::setUserResolver() does not exist on this mock object
Since I'm not calling directly setUserResolver()
from my controller, that means it is called directly by the implementation of Request
. But why? I mocked the method call, it shouldn't be calling any dependency.
由于我不是直接setUserResolver()
从我的控制器调用,这意味着它是由Request
. 但为什么?我嘲笑了方法调用,它不应该调用任何依赖项。
What am I doing wrong here, why am I getting this error message?
我在这里做错了什么,为什么会收到此错误消息?
PS: As a bonus, am I looking it the wrong way by forcing TDD with Unit Tests on Laravel framework, as it seems the documentation is oriented to integration testing by coupling interaction between dependencies and SUT with $this->call()
?
PS:作为奖励,我是否通过在 Laravel 框架上使用单元测试强制 TDD 来看待它,因为文档似乎通过将依赖项和 SUT 之间的交互与$this->call()
?
采纳答案by Amo
Unit testing a controller when using Laravel doesn't seem like a great idea. I wouldn't be concerned with the individual methods that are being called on the Request, Response and maybe even the repository classes, given the context of a Controller.
在使用 Laravel 时对控制器进行单元测试似乎不是一个好主意。考虑到控制器的上下文,我不会关心在请求、响应甚至存储库类上调用的各个方法。
Furthermore, unit testing a controller belonging to a framework because you want to decouple the test sut from its dependencies doesn't make sense, as you can only use that controller in that framework with those given dependencies.
此外,对属于框架的控制器进行单元测试是没有意义的,因为您希望将测试 sut 与其依赖项分离,因为您只能在具有给定依赖项的框架中使用该控制器。
As the request, response and other classes are all fully tested (either via the underlying Symfony class, or by Laravel itself), as a developer I'd concern myself only with testing the code I own.
由于请求、响应和其他类都经过了全面测试(通过底层 Symfony 类或 Laravel 本身),作为开发人员,我只关心测试我拥有的代码。
I'd write an acceptance test.
我会写一个验收测试。
<?php
use App\User;
use App\Page;
use App\Template;
use App\PageType;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class CategoryControllerTest extends TestCase
{
use DatabaseTransactions;
/** @test */
public function test__it_shows_a_paginated_list_of_categories()
{
// Arrange
$categories = factory(Category::class, 30)->create();
// Act
$this->visit('/categories')
// Assert
->see('Total categories: 30')
// Additional assertions to verify the right categories can be seen may be a useful additional test
->seePageIs('/categories')
->click('Next')
->seePageIs('/categories?page=2')
->click('Previous')
->seePageIs('/categories?page=1');
}
}
Because this test uses the DatabaseTransactions
trait, it's easy to perform the arrange part of the process, which almost allows you to read this as a pseudo-unit test (but that's a slight stretch of the imagination).
因为这个测试使用了DatabaseTransactions
trait,所以很容易执行这个过程的排列部分,这几乎可以让你把它看成一个伪单元测试(但这有点想象空间)。
Most importantly, this test verifies that my expectations are met. My test is called test_it_shows_a_paginated_list_of_categories
and my version of the test does exactly that. I feel the unit test route only asserts that a bunch of methods are called, but doesn't ever verify that I'm showing a list of the given categories on a page.
最重要的是,这个测试验证了我的期望是否得到满足。我的测试被调用test_it_shows_a_paginated_list_of_categories
,我的测试版本正是这样做的。我觉得单元测试路由只断言调用了一堆方法,但从未验证我是否在页面上显示给定类别的列表。
回答by Dylan Buth
You will always run into problems trying to properly unit test controllers. I recommend acceptance testing them with something like Codeception. Using acceptance tests you can ensure your controllers/views are handling any data appropriately.
在尝试正确地对控制器进行单元测试时,您总是会遇到问题。我建议使用Codeception 之类的东西对它们进行验收测试。使用验收测试可以确保您的控制器/视图正确处理任何数据。
回答by Ross_102
I tried mocking the Request for my tests too with no success. Here is how I test if item is saved:
我也尝试嘲笑我的测试请求,但没有成功。以下是我测试项目是否已保存的方法:
public function test__it_stores_a_category() {
$this->action(
'POST',
'CategoryController@store',
[],
[
'name' => 'foo',
]
);
$this->assertRedirectedTo('categories/admin/index');
$this->seeInDatabase('categories', ['name' => 'foo']);
}
Hope it is of some help
希望它有一些帮助