【问题标题】:Test a streamed response测试流式响应
【发布时间】:2019-01-22 06:01:12
【问题描述】:

我有以下路线:

Route::get('echo',function (Request $req) {
    return response()->stream(function () use ($req) {
        echo json_encode($req->all());
    }, 200, [
        'Content-Type' => 'application/json'
    ]);
})->name('echo');

为了简单起见,我们假设它是一个简单的回声响应。实际上,它是一个非常大的文件。两种情况的结果都是一样的。

现在我想测试这条路线,看看我是否确实可以看到那个 json 内容,所以我尝试了这个:

public function testBasicTest()
{

    $response = $this->get(route('echo', [
        "content"=>"some content"
    ]));
    $response->assertSeeText("some content"); //Doesn't work
    $response->assertJson( [
        "content"=>"some content"
    ]); //Neither does this
}

我已经进一步检查了它,这似乎是因为 (a) 响应包裹在 TestResponse 周围 (b) 响应内容永远不会流式传输 (c) 即使响应内容是通过$response->baseResponse->sendContent() 强制流式传输实际内容被回显,而不是由TestResponse 实际捕获

此外,调用$response->getContent() 也不起作用,因为它似乎直接调用了硬编码为返回false 的StreamedResponse::getContent()。

我使用以下方法取得了一些有限的成功:

ob_start();
$response->sendContent();
$result = ob_get_clean();

然而,这在单元测试中看起来是一件非常草率的事情。

有没有其他人遇到过这种情况?是否可以测试流式响应的内容?

【问题讨论】:

    标签: php laravel phpunit


    【解决方案1】:

    这不是一个好的解决方案,更像是一个 hack,但如果其他人遇到这个问题,你可以这样做:

    public function testBasicTest()
    {
    
        $response = $this->get(route('echo', [
            "content"=>"some content"
        ]));
        if ($response->baseResponse instanceof StreamedResponse) {
                ob_start();
                $response->sendContent();
                $content = ob_get_clean();
                $response = new TestResponse(
                    new Response($content, 
                                 $response->baseResponse->getStatusCode(), 
                                 $response->baseResponse->headers->all()
                    )
                );
        }
        $response->assertSee("some content"); //Works
    }
    

    【讨论】:

    • 这个问题是前段时间提出的:stackoverflow.com/q/15734677/7362396 - 也没有找到“更清洁”的解决方案:-(
    • @TobiasK 我不知道那个。我可能应该将搜索范围扩大到 symfony 而不仅仅是 Laravel
    • 没有批评你的回答。我认为这个问题没问题,因为它与其他问题不同,它指的是 Laravel。 StreamedResponse 在内部是一个 Symfony 的东西,可能不是每个人都清楚。只是想提供我发现的内容。
    • @TobiasK 我只是注意到更多的自我判断,您的评论实际上非常有帮助。
    【解决方案2】:

    聚会有点晚了,但可能对其他人有所帮助。

    在 Laravel 中,您可以在处理 StreamedResponse 时执行 $response->streamedContent()(我相信从 5.8 开始)。

    即使我的调试器告诉我我的响应的流式内容是null,我仍然可以从中获取数据。

    就我而言,它是 CSV,所以在我的功能测试中我已经完成了:

    $res = $this->post('api/v1/entity/export', $payload, $header);
    $res->assertStatus(200);
    $res->assertHeader('Content-Disposition', 'attachment; filename=entity.csv');
    
    $reader = Reader::createFromString($res->streamedContent());
    // tests...
    

    我使用了 LeagueCSV(显然它是 CSV),但我相信你也可以使用 Json 或其他方式。

    Laravel doc for TestResponse

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-01
      • 2021-01-24
      • 1970-01-01
      • 2020-04-17
      • 2020-05-07
      • 1970-01-01
      • 2022-12-14
      • 2016-05-15
      相关资源
      最近更新 更多