Generate your fixture file paths easier and less error-prone

Fixture files are a pretty common thing in web development. They allow you to produce "what should be" an end result, and assert that your test case matches that pre-produced end result.

Linking fixture files into your test cases via file paths typically becomes a messy endeavour and is prone to breaking if you ever modify the layout of your tests or fixtures. This often requires a find-and-replace solution in your code editor.

Linking fixtures this way becomes especially difficult when you're testing a Laravel package using Orchestra Testbench, since the file paths will actually be different when you run phpunit and you cannot use base_path() or other Laravel file path helper methods (they will return the path of the test Laravel application, not your actual packages base path).

To illustrate this, let's whip up a fictional test case that processes an audio file and "transcribes" it into a JSON file. Here's our package's file structure:

- package/
  - src/
  - tests/
    - fixtures/
        - transcription_audio.mp3
        - transcription_result.json
    - TranscriptionTest.php
    - TestCase.php
<?php

namespace Vendor\Package\Tests;

use Vendor\Package\Transcribe;

class TranscriptionTest extends TestCase
{
    public function testTranscriptionWorks()
    {
        $audio = __DIR__.'/fixtures/transcription_audio.mp3';
        
        $result = file_get_contents(
            __DIR__.'/fixtures/transcription_result.json'
        );
        
        $this->assertEquals($result, Transcribe::audio($audio));
    }
}

There's a couple issues with the above implementation:

  • If we move our fixtures folder, our test will break.
  • If we move TranscriptionTest to a sub-folder, our test will break.

To get around this, we can create a simple Fixture static class responsible for generating file paths to where our fixtures are located, with the help of Laravel's Storage::build() method:

<?php

namespace Vendor\Package\Tests;

use Illuminate\Support\Facades\Storage;

class Fixture
{
    /**
     * Get the file path to a fixure.
     *
     * @param string $file
     *
     * @return string
     */
    public static function path($file)
    {
        return Storage::build([
            'driver' => 'local',
            'root' => implode(DIRECTORY_SEPARATOR, [
                __DIR__, 'fixtures'
            ]),
        ])->path($file);
    }
}

Now we can re-write our above test in a much cleaner way:

<?php

namespace Vendor\Package\Tests;

use Vendor\Package\Transcribe;

class TranscriptionTest extends TestCase
{
    public function testTranscriptionWorks()
    {
        $audio = Fixture::path('transcription_audio.mp3');
        
        $result = file_get_contents(
            Fixture::path('transcription_result.json')
        );
        
        $this->assertEquals($result, Transcribe::audio($audio));
    }
}

This resolves the above issues:

  1. If we move our TranscriptionTest to a sub-folder, the test will pass
  2. If we move our fixtures to a new folder location, we can update the new file path in one place.

Thanks for reading!

Subscribe to Blog - Steve Bauman

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe