12

I have the following code in one of my routes:

return Response::download('cv.pdf');

Any idea how to test this? I've tried to use shouldReceive() but that doesn't seem to work ('shouldReceive() undefined function....').

sb89
  • 353
  • 1
  • 6
  • 23
  • 2
    The `Illuminate\Support\Facades\Response` class doesn't actually extend `Illuminate\Support\Facades\Facade` so doesnt have the `shouldRecieve()` method. You need to test the response of this route after calling it in a test. – David Barker Jan 13 '14 at 13:15
  • @DavidBarker I tested with laravel 8 and Response facade has `shouldRecieve()` now – Musa Haidari Apr 28 '21 at 19:01

4 Answers4

13

$response->assertDownload() was added in Laravel 8.45.0:

Assert that the response is a "download". Typically, this means the invoked route that returned the response returned a Response::download response, BinaryFileResponse, or Storage::download response:

$response->assertDownload();

Learn More:

https://laravel.com/docs/8.x/http-tests#assert-download

It does not currently (2023) allow one to test the contents of the file, but you can use test mocks for that:

    public function test_successful_download(): void
    {
        $this->authenticateAsAdmin();

        $this->instance(
            ResponseFactory::class,
            Mockery::mock(ResponseFactory::class, static function(MockInterface $mock) {
                $mock
                    ->shouldReceive('download')
                    ->with('[[ FILE CONTENTS ]]', '[[ FILE NAME]]')
                    ->once()
                ;
            }),
        );

        $this->call('POST', route('my-download-csv'));
    }
# mycontroller.php
// ...
    public function downloadCsv(Request $request)
    {
        return response()->download('[[ FILE CONTENTS ]]', '[[ FILE NAME]]');
    }
// ...
OK (1 test, 1 assertion)
raveren
  • 17,799
  • 12
  • 70
  • 83
Tanmay
  • 3,009
  • 9
  • 53
  • 83
  • 1
    Thank you for this information, but is there a way to test the "contents" of the file and not just the response headers? – Daryl Teo Jun 13 '21 at 14:36
  • (in my case, the endpoint for the download is a dynamically generated and signed file based on input parameters) – Daryl Teo Jun 13 '21 at 14:43
  • 1
    If the "content" has a mime type of `text/plain`, there might be a way to test the content. But if the content has something like `application/octet-stream` then I don't think you can test that. – Tanmay Jun 13 '21 at 15:37
  • Thanks. I realised what is actually happening (testing against the response object, not the output itself) and realised that what I was doing is stupid. If the content is dynamic it wouldn't be pulled from a static test file I was using, but it would be output in the content. So asserting against the content of the response I should be able to evaluate that the content is correct. Silly me, thank you. – Daryl Teo Jun 13 '21 at 21:49
3

EDIT: As pointed by @DavidBarker in his comment to the OP question

The Illuminate\Support\Facades\Response class doesn't actually extend Illuminate\Support\Facades\Facade so doesnt have the shouldRecieve() method. You need to test the response of this route after calling it in a test.


So if you want to test your download functionality, you can try checking the response for errors with:

$this->assertTrue(preg_match('/(error|notice)/i', $response) === false);
Gadoma
  • 6,475
  • 1
  • 31
  • 34
2

You can assert that the status code is 200

$this->assertEquals($response->getStatusCode(), 200);

because sometimes you might have some data returned that match "error" or "notice" and that would be misleading.

I additionally assert that there's an attachment in the response headers:

$this->assertContains('attachment', (string)$response);
Jad Joubran
  • 2,511
  • 3
  • 31
  • 57
  • 9
    For those from 2020: `$this->assertEquals('attachment; filename=cv.pdf', $response->headers->get('content-disposition'));` – Andrew P. Nov 29 '20 at 18:56
  • 2
    For those from 2023: `$response->assertHeader('content-disposition', 'attachment; filename=cv.pdf');` – Magmatic Mar 10 '23 at 19:16
1

You can use Mockery to mock the download method, for this you will need to mock ResponseFactory.

public function testDownloadCsv()
{
    $this->instance(
        ResponseFactory::class, Mockery::mock(ResponseFactory::class, function ($mock) {
        $mock->shouldReceive('download')
            ->once()
            ->andReturn(['header' => 'data']);
    }));

    $response = $this->get('/dowload-csv');

    $response->assertStatus(Response::HTTP_OK);
    $response->assertJson(['header' => 'data']); // Response
}