David Carr

Laravel how to test CSV download

When you have a CSV generated how do you test it runs. Take this extract:

public function export()
{
    $actions = $this->getData()->get();

    $columns = [
        'User',
        'Start Date',
        'End Date',
    ];

    $data = [];
    foreach ($actions as $action) {
        $data[] = [
            $action->user->name ?? '',
            $action->due_at,
            $action->completed_at,
        ];
    }

    $now      = Carbon::now()->format('d-m-Y');
    $filename = "actions-{$now}";

    return csv_file($columns, $data, $filename);
}

The above collects data and sends it to a helper function called csv_file which in turn will export a CSV file.

For completeness it contains:

if (! function_exists('csv_file')) {
    function csv_file($columns, $data, string $filename = 'export'): BinaryFileResponse
    {
        $file      = fopen('php://memory', 'wb');
        $csvHeader = [...$columns];

        fputcsv($file, $csvHeader);

        foreach ($data as $line) {
            fputcsv($file, $line);
        }

        fseek($file, 0);

        $uid = unique_id();

        Storage::disk('local')->put("public/$uid", $file);

        return response()->download(storage_path('app/public/'.$uid), "$filename.csv")->deleteFileAfterSend(true);

    }
}

Now there are 2 tests I want to confirm first, I get 200 status response to ensure the endpoint does not throw an error and secondly, the response contains the generated file name for the CSV.

To cater for the 200 status code run the endpoint and assertOk() which is a shortcut for a 200 instead of assertStatus(200)

Next check the header of the response and read the content-disposition header this will contain an attachment followed by the filename. Doing an assert true and comparing the header with the expected response;

test('can export actions', function () {
    $this->authenticate();

    $response = $this
        ->get('admin/reports/actions/csv')
        ->assertOk();

    $this->assertTrue($response->headers->get('content-disposition') == 'attachment; filename=actions-'.date('d-m-Y').'.csv');
});

Another way would be to use Pest's expect API:

$header = $response->headers->get('content-disposition');
$match = 'attachment; filename=actions-'.date('d-m-Y').'.csv';

expect($header)->toBe($match);

 

Laravel Modules Book by David Carr

Help support the blog so that I can continue creating new content!

Subscribe to my newsletter

Subscribe and get my books and product announcements.

© 2009 - 2022 DC Blog. All code MIT license. All rights reserved.