I have a class called ModuleManifest with this constructor:
/**
* Create a new instance of the ModuleManifest class.
*
* @param string $vendor The vendor of the module.
* @param string $package The name of the package.
* @return void
*/
public function __construct(string $vendor, string $package)
{
$manifest = $this->manifestPath($vendor, $package);
if (!File::exists($manifest))
{
throw new \InvalidArgumentException(sprintf(
'Package %s from vendor %s does not contain a manifest',
$package, $vendor
));
}
$this->manifest = $this->load($manifest);
}
This constructor loads a json file with the load method.
/**
* Load a module manifest into an object.
*
* @param string $path The path to the module.
* @return object
*/
protected function load(string $path) : object
{
return json_decode(file_get_contents($path));
}
/**
* Determine the path to the module manifest.
*
* @param string $vendor The vendor of the module.
* @param string $package The name of the package.
* @return string
*/
protected function manifestPath(string $vendor, string $package) : string
{
return implode('/', [base_path('vendor'), $vendor, $package, 'resources/manifest.json']);
}
I want determine whether the method is returning an object:
/** @testdox Returns an object when the path exists */
public function test_manifest_returns_object() : void
{
File::shouldReceive('exists')->once()->andReturnTrue();
$this->mock(ModuleManifest::class, function (MockInterface $mock) {
$mock->shouldAllowMockingProtectedMethods();
$mock->shouldReceive('load')->once()->andReturn(
json_decode('{"name": "testModule"}')
);
});
$manifest = new ModuleManifest('vendor', 'package');
$this->assertTrue($manifest->has('name'));
}
I get the error:
ErrorException: file_get_contents(...): Failed to open stream: No such file or directory
I would expect it to return the result of json_decode('{"name": "testModule"}'), but it tries to call the function normally. How to resolve this? I have tried to create a partial mock, but the issue remains.
Here is how I would do it. Add an optional
$basePathparameter to the constructor whose default value isbase_path():For the test, choose a test directory and create
vendor/foo/bar/resources/manifest.jsonin it. Then the testing code is simply:That's it. You don't need any mock. Also note that you should not test that
ModuleManifestuses theFileclass because it's just an implementation detail. It could very well do it another way (such as callingfile_exists()directly).