我可以使用Laravel的`call`方法在单元测试中发送原始JSON数据吗?

时间:2014-10-24 20:44:02

标签: php unit-testing

我正在尝试对Stripe webhook处理程序的实现进行单元测试。条带webhook数据作为POST请求正文中的原始JSON在线上传播,因此我捕获并解码数据:

public function store()
{
    $input = @file_get_contents("php://input");
    $request = json_decode($input);
    return Json::encode($request);
}

我试图对此代码进行单元测试,但我无法弄清楚如何在单元测试中发送原始JSON数据,以便我可以使用file_get_contents("php:input//")函数检索它。这是我尝试过的(使用PHPUnit):

protected $testRoute = 'api/stripe/webhook';

protected $validWebhookJson = <<<EOT
{
  "id": "ch_14qE2r49NugaZ1RWEgerzmUI",
  "object": "charge",
  // and a bunch of other fields too
}
EOT;

public function testWebhookDecdoesJsonIntoObject()
{
    $response = $this->call('POST', $this->testRoute, $this->validWebhookJson); // fails because `$parameters` must be an array
    $response = $this->call('POST', $this->testRoute, [], [], ['CONTENT_TYPE' => 'application/json'], $this->validWebhookJson);
    dd($response->getData(true)); // array(0) {} BOOOO!!! Where for to my data go?
}

我也试过了curl,但这会产生一个外部请求,从单元测试的角度来看,这对我没有意义。如何使用我的store方法获取的正文中的原始JSON数据来模拟POST请求?

6 个答案:

答案 0 :(得分:6)

你可以。但是你需要发送编码的JSON作为内容(也就是请求体)而不是参数。

$this->call(
    'POST',
    '/articles',
    [],
    [],
    [],
    $headers = [
        'HTTP_CONTENT_LENGTH' => mb_strlen($payload, '8bit'),      
        'CONTENT_TYPE' => 'application/json',
        'HTTP_ACCEPT' => 'application/json'
    ],
    $json = json_encode(['foo' => 'bar'])
);

这是第7个参数。

如果你看一下方法定义(在Laravel的核心中),你应该能够看到它的期望。

public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)

目前,Laravel 5.1的帖子,补丁,放置,删除等便捷方法目前尚不支持

目前正在讨论这项补充herehere

编辑:我应该声明此答案是基于Laravel 5.1安装的,因此如果您使用的是旧版本,则可能不会100%适用。

答案 1 :(得分:3)

您可以使用此处描述的json方法:

https://laravel.com/api/5.1/Illuminate/Foundation/Testing/TestCase.html#method_json

你可以看到第三个参数是一个数据数组,在这种情况下,它将作为json传递给请求体,如果需要传递额外的头,你可以在第四个参数中将它们作为数组传递

实施例: (在测试课程内)

public function testRequestWithJSONBody()
{
    $this->json(
            'POST', //Method
            '/', //Route
            ['key1' => 'value1', 'key2' => 'value2'], //JSON Body request
            ['headerKey1' => 'headerValue1','headerKey2' => 'headerValue2'] // Extra headers (optional)
        )->seeStatusCode(200);
}

希望这有助于其他人。

答案 2 :(得分:0)

使用Laravel 5.1,它很容易发送JSON,只需传递一个常规的PHP数组,它就会被自动编码。来自文档的示例:

$this->post('/user', ['name' => 'Sally'])
         ->seeJson([
            'created' => true,
         ]);

来自文档:http://laravel.com/docs/5.1/testing#testing-json-apis

答案 3 :(得分:0)

您可以覆盖CrawlerTrait中的post方法: https://laravel.com/api/5.1/Illuminate/Foundation/Testing/CrawlerTrait.html

或者创建一个新的辅助方法,如下所示,它接受一个额外的可选参数:rawContent

public function postRawContent($uri, array $data = [], array $headers = [], $rawContent = null)
{
    $server = $this->transformHeadersToServerVars($headers);

    $this->call('POST', $uri, $data, [], [], $server, $rawContent);

    return $this;
}

答案 4 :(得分:0)

我想测试从浏览器发布到后端的JSON。我想把原始的json放在phpunit中,所以我没有重新编码引入错误的数组。

为此,我首先将json对象转换为javascript(浏览器或客户端)中的字符串并将其转储到日志中:

console.log(JSON.stringify(post_data))

接下来,我将其复制并粘贴到phpunit测试中,然后将其解码为数组。然后我只是将该数组发送到json:

$rawContent = '{"search_fields":{"vendor_table_id":"123","vendor_table_name":"","vendor_table_account_number":"","vendor_table_active":"null"},"table_name":"vendor_table"}';

$this->json('POST', '/vendors', json_decode($rawContent, true))
     ->seeJson([
            'id' => 123,
        ]);

在实现这篇文章的其他答案之后,这是它对我有用的唯一方式,所以我想我会分享。我正在使用laravel 5.

答案 5 :(得分:0)

经过一些工作后我修好了,我已经完成了。



    $response = $this->call(
                'POST',
                "{$this->baseUrl}/{$this->version}/companies/{$company->id}",
                [
                    'name' => 'XPTO NAME',
                    'email' => 'xpto@example.org'
                ],
                [],
                [
                    'logo_image' => UploadedFile::fake()->image('teste.jpg', 200, 200),
                    'cover' => UploadedFile::fake()->image('teste.jpg', 1600, 570)
                ],
                [
                    'CONTENT_TYPE' => HttpContentType::MULTIPART_FORM_DATA,
                    'HTTP_ACCEPT' => HttpContentType::MULTIPART_FORM_DATA,
                    'HTTP_X_YOUR_TOKEN' => $authToken->token
                ]
            );
            $response->assertStatus(HttpStatusCodes::OK);