1

i'm having a bit of a problem with my PHPUnit integration tests, i have a method which handles a form upload for a video file as well as a preview image for that video.

public function store($request)
{
    /** @var Video $resource */
    $resource = new $this->model;

    // Create a new Content before creating the related Photo
    $contentRepo = new ContentRepository();
    $content = $contentRepo->store($request);

    if($content->isValid()) {
        $resource->content_id = $content->id;

        $directory = 'frontend/videos/assets/'.date("Y").'/'.date('m').'/'.time();
        \File::makeDirectory($directory, 0755, true);
        $request->video->move($directory.'/', $request->video->getClientOriginalName());
        $resource->video = '/'.$directory.'/'.$request->video->getClientOriginalName();
        $request->preview_image->move($directory.'/', $request->preview_image->getClientOriginalName());
        $resource->preview_image = '/'.$directory.'/'.$request->preview_image->getClientOriginalName();
        $resource->highlighted = intval($request->input('highlighted') == 'on');

        $resource->save();

        return $resource;
    }
    else {
        return $content;
    }
}

The important part to keep is the $request->video->move() call which i probably need to replace in order to use Virtual Filesystem.

and then the test

public function testVideoUpload(){
        File::put(__DIR__.'/frontend/videos/assets/image.mp4', 'test');
        $file = new UploadedFile(__DIR__.'/frontend/videos/assets/image.mp4', 'foofile.mp4', 'video/mp4', 100023, null, $test=true);

        File::put(__DIR__.'/frontend/images/assets/image.jpg', 'test');
        $preview = new UploadedFile(__DIR__.'/frontend/images/assets/image.jpg', 'foofile.jpg', 'image/jpeg', 100023, null, $test=true);

        $this->post('/admin/videos', [
            'title'  => 'My Video #12',
            'description' => 'This is a description',
            'actors' => [$this->actor->id, $this->actor2->id],
            'scenes' => [$this->scene->id, $this->scene2->id],
            'payment_methods' => [$this->paymentMethod->id],
            'video' => $file,
            'preview_image' => $preview
        ])->seeInDatabase('contents', [
            'title'  => 'My Video #12',
            'description' => 'This is a description'
        ]);
}

As you can see, i need to create a dummy file in some local directory and then use that in the HTTP request to the form's endpoint, then after that, that file would be moved and i need to delete the created folder and the new moved file... it's an authentic mess.

As such i want to use Virtual Filesystem instead, but i have no idea how to set it up in this particular case, i've already downloaded a package and set it up, but the questions are, first, which package have you used/recommend and how would you tweak the class and the test to support the Virtual Filesystem? Would i need to switch over to using the Storage facade instead of the $request->video->move() call? If so how would that be done exactly?

Thank you in advance for your help

João Serra
  • 509
  • 9
  • 21

1 Answers1

2

I couldn't figure out the VFS system, however i do have somewhat of an alternative that's still kinda messy but gets the job done.

Basically i set up two methods on my PHPUnit base class to setup and teardown the temp folders i need on any test that requires them, because i'm using Database Transactions the files get deleted on every test run and i need to create new dummy files every time i run the test.

So i have two methods setupTempDirectories and teardownTempDirectories which i will call at the beginning and at the end of each test that requires those temporary directories.

I put my temp files in the Storage directory because sometimes i run my tests individually through PHPStorm and the __DIR__ command gets messed up and points to different directories when i do that, i also tried __FILE__ with the same result, so i just resorted to using Laravel's storage_path instead and that works fine.

Then that leaves the problem of my concrete class which tries to move files around and create directories in the public folder for them... so in order to fix that i changed the code to use the Storage facade, then i Mock the Storage facade in my tests

So in my concrete class

        $directory = 'frontend/videos/assets/'.date("Y").'/'.date('m').'/'.time();
        Storage::makeDirectory($directory, 0755, true);

        Storage::move($request->video, $directory . '/' . $request->video->getClientOriginalName());
        $resource->video = '/'.$directory.'/'.$request->video->getClientOriginalName();

        Storage::move($request->preview_image, $directory . '/' . $request->preview_image->getClientOriginalName());
        $resource->preview_image = '/'.$directory.'/'.$request->preview_image->getClientOriginalName();

And then in my test i mock both the makeDirectory and the move methods like such

    // Override the Storage facade with a Mock version so that we don't actually try to move files around...
    Storage::shouldReceive('makeDirectory')->once()->andReturn(true);
    Storage::shouldReceive('move')->twice()->andReturn(true);

That makes my tests work and does not actually leave files behind after it's done...i hope someone has a better solution but for the time being this is what i came up with. I was actually trying to use VFS but it never worked out... i keep getting errors that the original file in the storage directory is not found even though it's right there...

I'm not even sure the Storage facade was using VFS in the background to begin with even though it should...

João Serra
  • 509
  • 9
  • 21