Laravel PHPUnit模拟请求

时间:2017-09-22 06:33:56

标签: php laravel unit-testing laravel-5 phpunit

我正在我的控制器上做一个PHPUnit,我似乎无法模拟请求。

这是控制器:

auto

然后在单元测试中,

use Illuminate\Http\Request;

public function insert(Request $request)
{
    // ... some codes here
    if ($request->has('username')) {
        $userEmail = $request->get('username');
    } else if ($request->has('email')) {
        $userEmail = $request->get('email');
    }
    // ... some codes here
}

这里的问题是,当我public function testIndex() { // ... some codes here $requestParams = [ 'username' => 'test', 'email' => 'test@test.com' ]; $request = $this->getMockBuilder('Illuminate\Http\Request') ->disableOriginalConstructor() ->setMethods(['getMethod', 'retrieveItem', 'getRealMethod', 'all', 'getInputSource', 'get', 'has']) ->getMock(); $request->expects($this->any()) ->method('get') ->willReturn($requestParams); $request->expects($this->any()) ->method('has') ->willReturn($requestParams); $request->expects($this->any()) ->method('all') ->willReturn($requestParams); // ... some codes here } 时,它总是返回整个数组中的var_dump($request->has('username');值。我期望它应该返回$requestParams,因为数组中存在用户名密钥。

然后当我删除true上的用户名密钥时,它应返回false,因为它不包含数组上的$requestParams密钥

3 个答案:

答案 0 :(得分:5)

模拟请求并不理想,但有时候你只想这样做:


1. { "pattern": "abcde*fgh"}
2. { "pattern": "test*new" }
3. { "pattern": "abc%^def" }

答案 1 :(得分:1)

据我所知和理解,您告诉您的单元测试当您在请求对象上调用$ request-> has()时它应该返回$ requestParams数组,而不是true或false,或其他任何东西。

除非你专门检查通过方法调用发送的内容,否则你的模拟实际上并不关心发送的是什么,它只关心它被调用。

您可能希望探索创建一个空的请求,并在您的用例中填写数据,因为这样可以让您更轻松,更少地运行单元测试。这在所有情况下都不会起作用。

您可以在单元测试中包含您所做的断言,这样我们就可以更清楚地了解您遇到的问题,但事实并非如此。它会准确地返回您要求它返回的内容。即使这不是你真正希望它返回的东西。

模拟用于将Unit-Test与系统的其他部分分开。因此,您通常只会检查是否调用了特定方法,以查看您的代码是否实际退出到您所模拟的类中,以及是否具有您要发送的预期数据。在某些极端情况下,您可能想要模拟您实际测试的系统,但这通常表明您的代码过于依赖其他类或者它做得太多。

使用模拟的另一个原因是在方法调用中满足Type Casting约束。在这些情况下,您通常会创建一个空的模拟对象,并在其中填充一些代码将接受或中断的虚拟数据来测试代码。

在你的情况下,似乎你想检查你的代码是否真的正常工作,为此我建议不要嘲笑请求,或者在你告诉它返回true或者做错的情况下进行特定的测试(测试为两种情况)

这就是:

$request->expects($this->any())
    ->method('has')
    ->with('username')
    ->willReturn(true); // or false in your next test

编辑: 正如您在评论中提到的那样,您遇到的问题是您在代码中多次使用has方法并遇到问题。

我在回复评论中链接的问题详细说明,但总结一下,您可以使用内联函数或at()方法来处理多个案例。

使用at(),您可以提供代码的特定迭代,以便仅测试该位。有人提到,这会使你的测试变得相当脆弱,因为之前的测试会破坏测试。

$request->expects($this->at(0))
    ->method('has')
    ->with('username')
    ->willReturn('returnValue');

$request->expects($this->at(1))
    ->method('has')
    ->with('email')
    ->willReturn('otherReturnValue');

内联函数(回调)解决方案允许您自定义测试以允许多个案例并根据需要返回数据。不幸的是,我对这个概念并不太熟悉,因为我之前没有使用它。我建议您阅读PHPUnit docs以获取更多相关信息。

最后,我仍然建议不要嘲笑请求,而是提出一个空的请求,说明您要填写要检查的数据。 Laravel提供了一些令人印象深刻的方法,可让您手动填写请求,并提供您经常测试的大量数据。

例如,您可以使用

添加数据(发布/获取数据)
request->add(['fieldname' => 'value'])

作为最后几点,我想提一下,你似乎使用了var_dump。 Laravel附带了两个自己的函数,它们在调试时非常相似且非常有用。 您可以使用dd();dump(); dd();转储并停止执行代码,而dump();只输出您决定的内容。所以你可以做dd($request);dump($request);并查看变量/类对象/等等。它甚至会把它放在一个相当漂亮的布局中,带有一些Javascript,这样可以让你看到它里面有什么等等。如果你不知道它存在,可能想看看它。

答案 2 :(得分:0)

如果情况更简单,则答案比@Ian更简单:

https://stackoverflow.com/a/61903688/135114, 如果

  1. 您正在测试的函数带有一个$request参数,并且
  2. 您不需要对Request做一些时髦的事情-实际的路线路径对您来说已经足够

...那么您无需“嘲笑”一个请求(例如嘲笑),
您可以创建一个Request 并通过它,例如

public function test_myFunc_condition_expectedResult() {
    ...
    $mockRequest = Request::create('/path/that/I_want', 'GET'); 
    $this->assertTrue($myClass->myFuncThat($mockRequest));
}