apokryfos
apokryfos

Reputation: 40681

Test a streamed response

I have the following route:

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

For the sake of simplicity lets assume it's a simple echo response. In reality it's a very large file. The outcome in either case is the same.

Now I want to test this route to see whether I indeed can see that json content so I've tried this:

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
}

I've inspected it a bit further and it appears to be because (a) the response is wrapped around a TestResponse (b) the response content is never streamed and (c) even if the response content were to be forcibly streamed via $response->baseResponse->sendContent() the actual content is echoed and not actually captured by the TestResponse

In addition calling $response->getContent() does not work because it seems to directly call the StreamedResponse::getContent() which is hard-coded to return false.

I have managed to have some limited success using:

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

however this looks like a very sloppy thing to do in a unit test.

Has anyone else encountered this before? Is it possible to test the contents of a streamed response?

Upvotes: 10

Views: 6574

Answers (2)

5th
5th

Reputation: 139

A bit late to the party but it may help someone else.

In Laravel you can do $response->streamedContent() when handling a StreamedResponse (since 5.8 I believe).

Even tho my debugger told me the streamed content of my response was null I still got the data out of it.

In my case it was CSV so in my functional tests I've done :

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

I used LeagueCSV (bc it was CSV, obviously) but I'm sure you can do the same with Json or other.

Laravel doc for TestResponse

Upvotes: 4

apokryfos
apokryfos

Reputation: 40681

This is not a good solution, more of a hack, but if anyone else encounters this issue here's what you can do:

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
}

Upvotes: 6

Related Questions