Проверка потокового ответа

У меня есть следующий маршрут:

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
}

Я проверил это немного дальше, и, похоже, это связано с тем, что (а) ответ обертывается вокруг TestResponse (б) содержимое ответа никогда не передается в потоковом режиме и (в) даже если содержимое ответа должно было принудительно передаваться через $response->baseResponse->sendContent() фактический контент воспроизводится, а не захватывается TestResponse

Кроме того, вызов $response->getContent() не работает, потому что кажется, что он напрямую вызывает StreamedResponse::getContent(), который жестко закодирован для возврата false.

Мне удалось добиться некоторого ограниченного успеха, используя:

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

однако это выглядит очень небрежно в модульном тесте.

Кто-нибудь еще сталкивался с этим раньше? Можно ли проверить содержимое потокового ответа?


person apokryfos    schedule 15.08.2018    source источник


Ответы (2)


Это не очень хорошее решение, скорее хак, но если кто-то еще столкнется с этой проблемой, вот что вы можете сделать:

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
}
person apokryfos    schedule 16.08.2018
comment
Этот вопрос возник некоторое время назад: stackoverflow.com/q/15734677/7362396 - и не было найдено более чистого решения: -( - person Tobias K.; 17.08.2018
comment
@TobiasK Я не знал об этом. Наверное, мне следовало расширить поиск на symfony, а не только на Laravel. - person apokryfos; 17.08.2018
comment
Нет критики к вашему ответу. Я думаю, что вопрос в порядке, поскольку он относится к Laravel, в отличие от другого вопроса. То, что StreamedResponse внутренне принадлежит Symfony, может быть не всем понятно. Просто хотел предоставить то, что я нашел в качестве контекста. - person Tobias K.; 17.08.2018
comment
@TobiasK Я просто отмечаю, что больше самоосуждения, ваш комментарий был на самом деле очень полезным. - person apokryfos; 17.08.2018

Немного поздно на вечеринку, но это может помочь кому-то еще.

В Laravel вы можете сделать $response->streamedContent() при обработке StreamedResponse (начиная с 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 для TestResponse

person 5th    schedule 14.04.2021