Reputation: 35360
I have a class that goes something like the following:
class Foo {
/**
* @var array|Bar
*/
protected $source;
public function __construct($source = []) {
if (class_exists(Bar::class)) {
$source = new Bar($source);
}
$this->source = $source;
}
public function getSource() {
return $this->source;
}
// ...
}
Bar
comes from a separate PHP package, an optional dependency listed in suggest
section of composer.json
.
I want to write two separate expectations in phpspec for getSource()
function it_provides_array_source_by_default() {
$this->getSource()->shouldBeArray();
}
function it_provides_bar_instance_when_available() {
$this->getSource()->shouldReturnAnInstanceOf(Bar::class);
}
I can't test the shouldBeArray()
if my require-dev
for the package includes the dependency containing Bar
.
I can't mock Bar
like:
function it_provides_bar_instance_when_available(Bar $bar) {
$this->getSource()->shouldReturnAnInstanceOf(Bar::class);
}
because I get a phpspec error:
[PhpSpec\Exception\Locator\ResourceCreationException] Can not find appropriate suite scope for class
Bar
.
What is the best-practice method for testing the return value being an optional class like this?
Upvotes: 0
Views: 54
Reputation: 522016
Move the logic that selects the type of $source
into an external factory function.
class Foo {
protected $source;
public function __construct($source = []) {
$this->source = $source;
}
}
class FooFactory {
public get() {
$source = [];
if (class_exists(Bar::class)) {
$source = new Bar($source);
}
return new Foo($source);
}
}
This allows you to explicitly test Foo
both ways and decouples the implementation by moving everything to a dependency injected model.
Upvotes: 1