6

Is there a way to mock a file using Laravels Storage::fake() method?

I have used https://laravel.com/docs/5.7/mocking#storage-fake as a base for my tests, which works fine for uploads. But my download tests are ugly as I have to run my upload route first every time with a mock upload UploadedFile::fake()->image('avatar.jpg'). Is there a way to skip that part and mock the file to exist directly in the fake storage system?

public function testAvatarUpload()
{
    Storage::fake('avatars');

    // This is the call I would like to change into a mocked existing uploaded file
    $uploadResponse = $this->json('POST', '/avatar', [
        'avatar' => UploadedFile::fake()->image('avatar.jpg')
    ]);

    // Download the first avatar
    $response = $this->get('/download/avatar/1');

    $response->assertStatus(200);
}
Sheph
  • 625
  • 1
  • 6
  • 19

4 Answers4

11

I might be late here. but wanted to help others visiting this question to give an idea of implementing it.

Here is a sample with some assertions.

<?php

namespace Tests\Feature\Upload;

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class SampleDownloadTest extends TestCase
{
    /**
     * @test
     */
    public function uploaded_file_downloads_correctly()
    {
        //keep a sample file inside projectroot/resources/files folder
        //create a file from it
        $exampleFile = new File(resource_path('files/test-file.png'))
        //copy that file to projectroot/storage/app/uploads folder
        Storage::putFileAs('/uploads', $exampleFile, 'test-file.png');

        //make request to file download url to get file 
        $response = $this->get("/files/file/download/url");

        //check whethe response was ok
        $response->assertOk();
        $response->assertHeader('Content-Type', 'image/png')
        //check whether file exists in path
        Storage::assertExists('/uploads/test-file.png');
        //do some more assertions.....
        //after test delete the file from storage path
        Storage::delete('uploads/test-file.png');
        //check whether file was deleted
        Storage::assertMissing('/uploads/test-file.png');
    }
}
aimme
  • 6,385
  • 7
  • 48
  • 65
4

Yes, you can use fake file storage feature of Laravel (mocking):

use Illuminate\Http\UploadedFile;

$file = UploadedFile::fake()->create('filename.ext', $sizeInKb)->store('filename.ext');

If you want to create a text/csv file with a specific content you can use this:

use Illuminate\Http\UploadedFile;

$header = 'a,b,c';
$row1 = 'x,y,z';
$row2 = 's,f,t';
$row3 = 'r,i,o';

$content = implode("\n", [$header, $row1, $row2, $row3]);

$file = UploadedFile::fake()->createWithContent('filename.ext', $content)->store('filename.ext');


You can find this methods definitions in Illuminate\Http\Testing\FileFactory

Alessandro
  • 409
  • 5
  • 12
1

You could just create a new file directly or copy a specific test file for example:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;

// for simple text files or if the content doesn't matter
Storage::disk('avatars')->put('avatar.jpg', 'some non-jpg content');

// if you need a specific file for your test
$file = new File(base_path('tests/resources/avatar.jpg'));
Storage::disk('avatars')->putFileAs('/', $file, 'avatar.jpg');

The latter function will take the $file and copy it under the given name avatar.jpg to the given directory / on the disk avatars. You can read more about it in the official documentation.

Namoshek
  • 6,394
  • 2
  • 19
  • 31
0

What you could use to solve that problem is fixtures. Laravel's testing framework is essentially PHPUnit, so I see no reason why it would not work.

define your test like so:

use Tests\TestCase;

class ExampleTest extends TestCase {
    protected function setUp() {
        parent::setUp();
        Storage::fake('avatars');
        $uploadResponse = $this->json('POST', '/avatar', [
          'avatar' => UploadedFile::fake()->image('avatar.jpg')
        ]);
    }
    protected function tearDown() {
        parent::tearDown();
    }
    public function testAvatarUpload() {
        // Download the first avatar
        $response = $this->get('/download/avatar/1');

        $response->assertStatus(200);
    }
}

setUp and tearDown get called, respectively, before and after each test in the class. So, before each test method, setUp will wipe the avatars fake disk and run the request. As there is nothing to do after a test (since Storage::fake() replaces the disk if it already exists), the method is empty; I left it here purely to make the example complete.

There's some pretty good documentation on here about this feature of PHPunit.

Regarding putting the file on there, once you have your setUp working correctly, there's nothing stopping you from throwing the file on it.

Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66