【问题标题】:Can I use Laravel's `call` method to send raw JSON data in a unit test?我可以使用 Laravel 的 `call` 方法在单元测试中发送原始 JSON 数据吗?
【发布时间】:2014-12-20 18:24:25
【问题描述】:

我正在尝试对我的 Stripe webhook 处理程序的实现进行单元测试。 Stripe 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,但这会发出一个外部请求,从单元测试的角度来看,这对我来说没有意义。如何模拟 POST 请求,其中包含我的 store 方法将拾取的正文中的原始 JSON 数据?

【问题讨论】:

  • 第二次调用中的技术对我来说是正确的。我猜“一堆其他字段”的格式不正确,所以你基本上回显的 json 是空的。当然,截断示例中的尾随逗号不是有效的 json。我将从 xdebugging store() 开始。

标签: php unit-testing


【解决方案1】:

经过一些工作,我修复并完成了。

$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);

【讨论】:

    【解决方案2】:

    你可以。但是您需要将编码后的 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 的 post、patch、put、delete 的便捷方法目前不支持

    目前正在讨论此添加 herehere

    编辑:我应该声明这个答案是基于 Laravel 5.1 安装的,所以如果你使用的是旧版本,它可能不是 100% 适用于你。

    【讨论】:

    • 这仍然适用于 Laravel 5.4;但是,标头必须是CONTENT_TYPE,而不是HTTP_CONTENT_TYPE。请注意,所有其他标头(包括授权和其他标头)必须以 HTTP_ 开头。
    【解决方案3】:

    我想测试从浏览器发布到后端的 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。

    【讨论】:

      【解决方案4】:

      您可以覆盖 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;
      }
      

      【讨论】:

        【解决方案5】:

        您可以使用此处描述的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);
        }
        

        希望这对其他人有所帮助。

        【讨论】:

        • 您不能使用这种方法传递原始 JSON 字符串,只能传递一个数组。
        【解决方案6】:

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

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

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

        【讨论】:

        • 不要认为这是对的。文档实际上说传递给seeJson() 的数组将被自动解析,而不是参数字段的数组。在 Lumen(基本上是 Laravel)的 github 问题 here 上也提出了类似的问题,laravel 的一位常规贡献者表示您必须手动对其进行编码。
        猜你喜欢
        • 2016-08-22
        • 2020-01-05
        • 1970-01-01
        • 2016-12-24
        • 2016-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多