Smiffy's avatar

Best way to test file creation

I am creating a command in a package which ultimately installs a migration. What I am having trouble with is testing that the file exists and the contents of that file. I don't really want to have to physically write the file to an arbitrary location and clean up if I can help it.

My command (just the important bit):

...
use Illuminate\Filesystem\Filesystem;
...
public function construct(Filesystem $files)
{
    $this->files = $files;
}

public function handle()
{
    $this->files->put($location, $content);
}
...

For my test I started out mocking File which enables to check that put is called and that the call was successful but that's about it.

it('will create the migration' function() {
    File::shouldReceive('put')
        ->once()
        ->andReturnTrue();
});

I'd like to be able to verify a few things in the created file to ensure the command is doing what it's supposed to be doing.

One option I have considered is to create an "On Demand" disk in my test pointing to the migrations directory (https://laravel.com/docs/9.x/filesystem#on-demand-disks ) but unsure if this is the best way. I have also seen vfstream metioned a few times, but again, unsure if this is a good fit or, more pointedly, there is a better way for Laravel. Any help on best practice appreciated!

0 likes
3 replies
martinbean's avatar

@smiffy Mock the filesystem and set expectations for the write location and contents.

Smiffy's avatar

I think that's where I fall down with my understanding. I reworked the code to:

    $this->mock(Filesystem::class, function ($mock) use ($filename) {
        $mock->shouldReceive('put')
            ->once()
            ->andReturnTrue();

        //this was just a thought but ultimately it failed. Take it out and it passes but back to not being able to test generation/content
        expect(app(Filesystem::class)->exists(database_path('migrations').'/'.$filename))->toBeTrue();
    });
martinbean's avatar
Level 80

@Smiffy You’re meant to set the expectations in that $this->mock call.

public function __construct(Filesystem $files)
{
    $this->files = $files;
}

public function handle()
{
    $this->files->put($location, $content);
}
$location = 'location/file/expected/to/be/saved/to';
$contents = 'Expected contents of file';

$this->mock(Filesystem::class, fn (MockInterface $mock) => $mock
    ->shouldReceive('put')
    ->once()
    ->with($location, $content)
    ->andReturnTrue()
);

$this->artisan('whatever:command')->assertSuccessful();
1 like

Please or to participate in this conversation.