Reputation: 1847
I have a class that creates a file via a named constructor but when I test it using phpspec it does not create the file.
I can't find the reason for that, so I guess a fresh look at my code could help.
Here's my File class:
<?php
namespace Acme;
class File
{
/**
* @var Path
*/
private $path;
/**
* @var FileName
*/
private $fileName;
private function __construct(Path $path, FileName $fileName)
{
$this->path = $path;
$this->fileName = $fileName;
}
public static function create(Path $path, FileName $fileName)
{
if (file_exists((string) $path . (string) $fileName)) {
throw new \DomainException('File already exists');
}
if (!touch((string) $path . (string) $fileName)) {
throw new \DomainException('Cannot create file');
}
return new self($path, $fileName);
}
}
Here my spec:
<?php
namespace spec\Acme;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Acme\Path;
use Acme\FileName;
class FileSpec extends ObjectBehavior
{
private $testPath;
private $existingFileName = 'existingFile.extension';
private $nonExistingFileName = 'nonExistingFile.extension';
private $existingFilePath;
private $nonExistingFilePath;
function let()
{
$this->testPath = sys_get_temp_dir() . '/';
$this->existingFilePath = $this->testPath . $this->existingFileName;
$this->nonExistingFilePath = $this->testPath . $this->nonExistingFileName;
// Creating existing file
if (!touch($this->existingFilePath)) {
throw new \Exception('Cannot create existing file for testing');
}
// Removes non existing file
if (file_exists($this->nonExistingFilePath)) {
if (!unlink($this->nonExistingFilePath)) {
throw new \Exception('Cannot remove non existing file for testing');
}
}
}
function it_does_not_create_a_file_when_the_file_already_exists(Path $path, FileName $fileName)
{
$path->__toString()->willReturn($this->testPath);
$fileName->__toString()->willReturn($this->existingFileName);
$this->beConstructedThrough('create', [$path, $fileName]);
$this->shouldThrow(new \DomainException('File already exists'))->duringInstantiation();
}
function it_creates_a_new_file_if_file_does_not_exist(Path $path, FileName $fileName)
{
$path->__toString()->willReturn($this->testPath);
$fileName->__toString()->willReturn($this->nonExistingFileName);
$this->beConstructedThrough('create', [$path, $fileName]);
assert(file_exists($this->nonExistingFilePath));
}
}
Upvotes: 0
Views: 367
Reputation: 2339
This is because phpspec will not instantiate the class until required to do so. Only method calls or expectations (i.e. should*
) against the original class itself will result in in it being instantiated, and beConstructedThrough
is simply a hint as to how it phpspec should gain an instance.
Now, you could work around this by calling into some method, or perhaps even just calling $this->shouldHaveType(File::class)
, but I'd suggest rethinking the approach. If you're ultimately integrating into something external -- be it an SDK, filesystem, database, etc, you'll be much better off writing an integration test. Which you're pretty close to doing in this case anyway (mocking shouldn't really be necessary). phpspec is more aimed towards specifying the behaviour/logic of classes and methods.. describing side effects does not really fit within it's remit. Usage of assert()
here is a hint towards that too, as this is certainly not idiomatic for phpspec backed specifications.
For an integration test, PHPUnit would be a better choice, as it's more general purpose.. so you'll have the flexibility to instantiate and assert as required.
Upvotes: 2