I'm new to unit testing and PHPUnit, but I've reading a lot lately about design patterns and isolated tests and I've decided to refactor an application I'm working on to get rid of static classes, singletons, hardcoded dependencies and anything else defined on the global scope, hopefully making it "testable" and not a pain in the ass to mantain in the future, since it is meant to be a long term project.
So far I believe I understand the theory behind unit testing, but I was wondering, in a scenario where one delegates handling nested dependencies of objects to a Factory, how should one go about unit testing said Factory, or is it just redundant to test it? And what is the best approach to test that the "chain" of dependencies work well in sync?
Let me illustrate the questions. Suppose you have the following "legacy" code:
class House {
protected $material;
protected $door;
protected $knob;
public function __construct() {
$this->door = new Door();
$this->knob = $this->door->getKnob();
$this->material = "stone";
echo "House material: ".$this->material . PHP_EOL . "<br/>";
echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
}
}
class Door {
protected $material;
protected $knob;
public function __construct() {
$this->knob = new Knob();
$this->material = "wood";
}
public function getKnob() {
return $this->knob;
}
public function getMaterial () {
return $this->material;
}
}
class Knob {
protected $material;
public function __construct() {
$this->material = "metal";
}
public function getMaterial () {
return $this->material;
}
}
$house = new House();
This is (as far as my understanding goes) bad for unit testing, so we replace the hardcoded dependencies with DI + a Factory class:
class House {
protected $material;
protected $door;
protected $knob;
public function __construct($door) {
$this->door = $door;
$this->knob = $this->door->getKnob();
$this->material = "stone";
echo "House material: ".$this->material . PHP_EOL . "<br/>";
echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
}
}
class Door {
protected $material;
protected $knob;
public function __construct($knob) {
$this->knob = $knob;
$this->material = "wood";
}
public function getKnob() {
return $this->knob;
}
public function getMaterial () {
return $this->material;
}
}
class Knob {
protected $material;
public function __construct() {
$this->material = "metal";
}
public function getMaterial () {
return $this->material;
}
}
class HouseFactory {
public function create() {
$knob = new Knob();
$door = new Door($knob);
$house = new House($door);
return $house;
}
}
$houseFactory = new HouseFactory();
$house = $houseFactory->create();
Now (and again, as far as I understand) House, Door and Knob can be unit tested with mocked dependencies just fine. But:
1) What happens with HouseFactory now?
Should one just:
2) Is it feasible to set up tests that rely on several (not mocked) dependencies at once? I understand this is technically not unit testing (integration testing perhaps?) but I guess it's still perfectly doable using PHPUnit? Given the example above, I would like to be able to set up a test that not only tests House, Door, Knob and HouseFactory in isolation, but also the results of the interaction of the real objects with each other, perhaps with some of their functions mocked, such as the ones which deal with data. Is PHPUnit a bad choice for this kind of tests?
Thanks in advance for your time. I realize some of the assumptions I'm making may not be correct, since I'm obviously not an expert on the matter; corrections are welcome and appreciated.
The factory is just like the new
keyword. Do you test the new
keyword? No, you test if you can construct a class. But that's independent to the factory itself and part of the unit so already part of your unit tests.
2) is called integration testing. And you can do that with PHPUnit as well.
Edit - As there was some discussion in comments:
As far as Unit testing is concerned, you could unit-test your factory that it does for what it is for: return a concrete type, a type or any type at all.
There is nothing wrong with that, however it's normally not necessary as constructors of the returned type(s) are already under unit-tests and well that test is really trivial and just data-checking which smells like integration testing. Also those types which have that type from the factory as dependency (and which are under unit-test as well) will make compilation/execution fail if the dependency can not be provided. So everything the factory is for, is already tested, even from both sides. And if the factory does not get consumed, well then you don't need to test it.
I suggest you create once a factory purely TDD style, so to pre-formulate the use and then you'll get a feeling for this. You might want to test other aspects of your factory class(es), but probably this belongs more into integration than unit testing.
And I don't wanted to create the impression that other of your units should actually have hardcoded calls to the factory create method(s) instead of getting the dependency injected. As you should not use new
inside your units, you should not use Factory::create
therein either. Similar to new
, the class-name (Factory
) is hard-encoded, not injected. It is a hidden dependency then. But dependencies should not be hidden; but made visible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With