Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 615
apokryfos Avatar asked Aug 15 '18 10:08

apokryfos


2 Answers

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
}
like image 81
apokryfos Avatar answered Sep 28 '22 13:09

apokryfos


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

like image 36
5th Avatar answered Sep 28 '22 12:09

5th