Testing and developing a Codeception module 04

Testing for file inclusion or requirement.

The problem

Writing Codeception tests for a Codeception module makes me face some uncommon testing challenges that do no get much spotlight when talking about tests.
In the process of testing the wp-embedded module I’ve come to the point of having to test the method below

/**
 * Requires the main plugin file and symlinks it in the embedded
 * WP installation folder.
 *
 * @throws ModuleConfigException If the specified main plugin file was not found
 * in the the project root folder.
 *
 * @return void
 */
public function loadMainPlugin()
{
    if (empty($this->config['mainFile'])) {
        return;
    }

    $path = $this->getMainPluginFileAbspath();

    /** @noinspection PhpIncludeInspection */
    require_once $path;

    $this->symlinkMainPluginFile($path);
}

The main plugin file is specified in the EmbeddedWP configuration

modules:
    config:
        EmbeddedWp:
            mainFile: my-plugin.php

and I’d like to be able to specify it using a relative path like in the case above or using an absolute path

modules:
    config:
        EmbeddedWp:
            mainFile: /Users/Me/Plugins/my-plugin/my-plugin.php

I need to test the code above and make sure the main plugin file is required.
Sadly no assertFileRequired($file) or assertFileIncluded($file) method exists in PhpUnit.
While I could split the method down into the file requiring and the file symlinking functions that would still leave me with tests that succeed by lack of failure opening the door to potential false positives.
If nothing goes wrong in a test PhpUnit will mark it as passing; a test like the one below will pass

/**
 * an empty test passes
 */
public function test_an_empty_test_passes()
{
}    

And while I know exceptions will be thrown if the file is not accessible or not existent in the loadMainPlugin method I still want to make sure the plugin file is being required.

Virtual file system to the rescue

As I’ve pointed out in an earlier post when having to test anything dealing with the filesystem a virtual file system like vfsstream is the way to go and in the case of the method above it comes very handy.
In the tests _before method, Codeception version of PhpUnit setUp method, I will define the virtual file system structure

protected function _before()
    {
        Test::setUp();
        $structure = [
            'Users' => [
                'Me' => [
                    'cloned-plugins' => [
                        'plugin-a' => [
                            'plugin-a.php' => '<?php //plugin-a'
                        ]
                    ]
                ]
            ],
            'my-plugin' => [
                'vendor' => [
                    'required-plugins' => ['plugin-b' => ['plugin-b.php' => '<?php // plugin-b']]
                ],
                'lucatume' => [
                    'wp-embedded' => [
                        'src' => [
                            'embedded-wordpress' => [
                                // EmbeddedWp will look up this file to make sure this is a valid WP install
                                'wp-settings.php' => '<?php // wp-settings.php'
                            ]
                        ]
                    ]
                ],
                'my-plugin.php' => "<?php define('MYPLUGINREQUIRED', 1);"
            ]
        ];
        VfsStream::setup('folder_tree', null, $structure);
    }

and will set the contents of the file at /my-plugin/my-plugin.php to

<?php define('MYPLUGINREQUIRED', 1);

The virtual file will be included by the code in the loadMainPlugin method and the define instruction will be executed.
I can now write a test to be sure the file requirement is happening

/**
 * @test
 * it should allow for the main plugin file to be specified as abspath
 */
public function it_should_allow_for_the_main_plugin_file_to_be_specified_as_abspath()
{
    // pre-test check
    $this->assertFalse(defined('MYPLUGINREQUIRED'));

    // set up
    $projectRoot = VfsStream::url('folder_tree') . '/my-plugin';
    $mainPluginFilePath = $projectRoot . '/my-plugin.php';
    $config = ['mainFile' => $mainPluginFilePath];
    $filesystem = Test::replace('Symfony\Component\Filesystem\Filesystem')->method('symlink')->get();
    $embeddedWpPath = $projectRoot . '/vendor/lucatume/wp-embedded/src/embedded-wordpress';
    $pathFinder = new Paths($projectRoot, $embeddedWpPath);
    $sut = new EmbeddedWP(make_container(), $config, $pathFinder, $filesystem);

    // execute
    $sut->loadMainPlugin();

    // verify
    $this->assertTrue(defined('MYPLUGINREQUIRED'));
    $this->assertEquals(1, MYPLUGINREQUIRED);
}

While this might look like overkill here the trick might come handy when dealing with multiple files that have to be loaded in a certain order.