Mah*_*ahn 16 php phpunit unit-testing design-patterns
我是单元测试和PHPUnit的新手,但我最近读了很多关于设计模式和隔离测试的内容,我决定重构一个我正在努力摆脱静态类,单例,硬编码依赖项的应用程序.在全球范围内定义的任何其他内容,希望使其"可测试",而不是在未来保持屁股的痛苦,因为它本来是一个长期项目.
到目前为止,我相信我理解单元测试背后的理论,但我想知道,在一个委托处理对象的嵌套依赖关系到一个工厂的场景中,应该如何进行单元测试所述工厂,或者它是多余的来测试它?什么是测试依赖关系"链"同步工作的最佳方法?
让我来说明问题.假设您有以下"遗留"代码:
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();
Run Code Online (Sandbox Code Playgroud)
这是(据我的理解)对单元测试不利,所以我们用DI + Factory类替换硬编码的依赖:
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();
Run Code Online (Sandbox Code Playgroud)
现在(并且据我所知),House,Door和Knob可以使用模拟依赖项进行单元测试.但:
1)HouseFactory现在会发生什么?
应该只是:
2)设置依赖于多个(非模拟)依赖项的测试是否可行?我知道这在技术上不是单元测试(也许是集成测试?)但是我想它仍然可以使用PHPUnit完美地完成?鉴于上面的例子,我希望能够设置一个测试,不仅可以隔离测试House,Door,Knob和HouseFactory,还可以测试真实物体之间相互作用的结果,也许还有一些模拟的函数,例如处理数据的函数.PHPUnit对于这种测试来说是一个糟糕的选择吗?
在此先感谢您的时间.我意识到我所做的一些假设可能不正确,因为我显然不是这方面的专家; 我们欢迎并赞赏更正.
工厂就像new关键字一样。你测试new关键字吗?不,你测试是否可以构建一个类。但这独立于工厂本身和单元的一部分,因此已经是单元测试的一部分。
2)称为集成测试。您也可以使用 PHPUnit 来做到这一点。
编辑-正如评论中进行了一些讨论:
就单元测试而言,您可以对工厂进行单元测试,看看它的用途:返回一个具体类型、一个类型或任何类型。
这没有什么问题,但是通常没有必要,因为返回类型的构造函数已经在进行单元测试,而且该测试确实很简单,只是数据检查,闻起来像集成测试。此外,如果无法提供依赖项,那些将工厂中的类型作为依赖项(并且也在单元测试中)的类型也会使编译/执行失败。因此,工厂的一切都已经经过测试,甚至是来自双方的测试。如果工厂没有被消耗,那么你就不需要测试它。
我建议你创建一次纯粹的TDD风格的工厂,这样就可以预先制定使用方式,然后你就会对此有一个感觉。您可能想测试工厂类的其他方面,但这可能更多地属于集成而不是单元测试。
我不想给人留下这样的印象:您的其他单元实际上应该对工厂创建方法进行硬编码调用,而不是注入依赖项。由于您不应该new在您的单位内使用,所以您也不应该Factory::create在其中使用。与 类似new,类名 ( Factory) 是硬编码的,而不是注入的。那么它就是一个隐藏的依赖。但依赖关系不应该被隐藏;但变得可见。