Laravel PHPUnit 模拟请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46358264/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 16:41:36  来源:igfitidea点击:

Laravel PHPUnit mock Request

phplaravelunit-testinglaravel-5phpunit

提问by basagabi

I'm doing a PHPUnit on my controller and I can't seem to mock the Request right.

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

Here's the Controller:

这是控制器:

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
}

Then on the unit test,

然后在单元测试中,

public function testIndex()
{
    // ... some codes here

    $requestParams = [
        'username' => 'test',
        'email'    => '[email protected]'
    ];

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

The problem here is that when ever I var_dump($request->has('username');it always return the $requestParamsvalue in which is the whole array. I'm expecting that it should return trueas the username key exists in the array.

这里的问题是,当我var_dump($request->has('username');总是返回$requestParams整个数组的值时。我期望它应该返回,true因为用户名键存在于数组中。

Then when I delete the username key on the $requestParams, it should return false as it does not contain the usernamekey on the array

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

采纳答案by Tropus

As far as I can see and understand you're telling your unit test that when you call $request->has() on your request object that it should return the $requestParams array, not true or false, or anything else.

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

Unless you specifically check what is send with a method call your mock doesn't actually care what is send, it just cares that it was called.

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

You might want to explore creating an empty request and filling it with data if that is possible in your use case as that'll let you run your unit test with more ease and less issues. This won't work in all cases.

如果在您的用例中可能的话,您可能想要探索创建一个空请求并用数据填充它,因为这将使您更轻松地运行单元测试并减少问题。这不会在所有情况下都有效。

You could include what assertions you're making in your unit test so we can see more clearly what you're running into, but as it is. It returns exactly what you're telling it to return. Even if that's not what you actually want it to return.

您可以在单元测试中包含您所做的断言,以便我们可以更清楚地看到您遇到的情况,但实际上是这样。它准确地返回您告诉它返回的内容。即使那不是您真正希望它返回的内容。

Mocks are used to separate your Unit-Test from the rest of your system. As such you usually tend to only check if a specific method is called to see if your code actually exits to the class you mocked and if it has the expected data you'd send along. In some extreme cases you can want to mock the system you're actually testing, but this usually indicates that your code is too dependent on other classes or it's doing too much.

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

Another reason to use mocks is to satisfy Type Casting constraints in your method calls. In these cases you'll usually create an empty mocked object and fill it with some dummy data your code will accept or break on to test the code.

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

In your case it seems you want to check if your code actually works correctly and for this I'd suggest either not mocking the request, or making specific tests where you tell it to return true, or false (test for both cases)

在您的情况下,您似乎想检查您的代码是否确实正常工作,为此我建议您不要模拟请求,或者进行特定测试,告诉它返回真或假(测试两种​​情况)

So something along the lines of:

所以类似的东西:

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

Edit: As you mentioned in the comment Below you ran into the issue that you're using the has method multiple times in your code and ran into issues.

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

The Questions I've linked to in my response comment go into greater detail but to sum it up, you can use an inline function or the at() method to deal with multiple cases.

我在回复评论中链接的问题更详细,但总而言之,您可以使用内联函数或 at() 方法来处理多种情况。

With at() you can supply specific iterations of the code to hit only that bit of the test. It has been mentioned that this makes your tests rather brittle as any has added before the previous ones would break the test.

使用 at() 您可以提供代码的特定迭代以仅命中测试的那部分。已经提到这会使您的测试变得相当脆弱,因为在先前的测试会破坏测试之前添加任何测试。

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

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

The inline function (callback) solution would allow you to customize your test to allow multiple cases and to return data as required. Unfortunately I'm not too familiar with this concept as I haven't used it myself before. I suggest reading the PHPUnit docsfor more information about this.

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

In the end I'd still suggest not mocking the request and instead making an empty request that you'll fill with the data you want to check. Laravel comes with some impressive methods that'll let you manually fill the request with a lot of data you'd usually test against.

最后,我仍然建议不要嘲笑请求,而是提出一个空请求,您将使用要检查的数据填充。Laravel 提供了一些令人印象深刻的方法,它们可以让您手动填充请求,其中包含通常用于测试的大量数据。

For example you can add data (post/get data) by using

例如,您可以使用添加数据(发布/获取数据)

request->add(['fieldname' => 'value'])

As a last few pointers I'd like to mention that it seems you use var_dump. Laravel comes with two of it's own functions that are similar and quite useful in debugging. You can use dd();or dump();dd();dumps and stops the execution of code, while dump();just outputs whatever you decide. so you could do dd($request);or dump($request);and see what the variables/class objects/etc holds. It'll even put it in a rather spiffy layout with some Javascript and such to allow you to see what's in it and such. Might want to check it out if you didn't knew it existed.

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

回答by Ian

Its not ideal to mock Requests, but sometimes you just want to do it anyway:

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

protected function createRequest(
    $method,
    $content,
    $uri = '/test',
    $server = ['CONTENT_TYPE' => 'application/json'],
    $parameters = [],
    $cookies = [],
    $files = []
) {
    $request = new \Illuminate\Http\Request;
    return $request->createFromBase(
        \Symfony\Component\HttpFoundation\Request::create(
            $uri,
            $method,
            $parameters,
            $cookies,
            $files,
            $server,
            $content
        )
    );
}