gpt4 book ai didi

php - 使用嵌套依赖项和工厂类执行单元测试

转载 作者:IT王子 更新时间:2023-10-29 00:18:41 27 4
gpt4 key购买 nike

我是单元测试和 PHPUnit 的新手,但我最近阅读了很多关于设计模式和独立测试的文章,我决定重构我正在开发的应用程序以摆脱静态类、单例、硬编码依赖项和在全局范围内定义的任何其他内容,希望使其“可测试”并且在未来维护起来不会很痛苦,因为它意味着一个长期项目。

到目前为止,我相信我理解了单元测试背后的理论,但我想知道,在将处理对象的嵌套依赖项委托(delegate)给工厂的场景中,应该如何进行单元测试,或者说它只是多余的测试它?测试依赖关系“链”是否同步工作的最佳方法是什么?

让我来说明问题。假设您有以下“遗留”代码:

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();

这(据我所知)不利于单元测试,因此我们用 DI + 工厂类替换硬编码的依赖项:

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();

现在(据我所知)House、Door 和 Knob 可以使用模拟依赖项进行单元测试。但是:

1) HouseFactory 现在怎么样了?

应该是:

  • 不测试它,因为它还没有任何值得测试的应用程序逻辑,而且工厂通常保持这种状态。假设如果房屋、门和 Handlebars 的独立测试通过了工厂应该没问题。
  • 以某种方式重构工厂,即使用类中的函数来获取每个实例,以便可以通过 PHPUnit 覆盖这些函数以返回模拟对象,以防万一类中有一些额外的逻辑可以使用将来进行一些测试。

2) 设置同时依赖多个(非模拟)依赖项的测试是否可行?我知道这在技术上不是单元测试(也许是集成测试?)但我想它仍然可以使用 PHPUnit 完全可行吗?鉴于上面的示例,我希望能够设置一个测试,不仅单独测试 House、Door、Knob 和 HouseFactory,而且还测试真实对象彼此交互的结果,也许与它们的某些模拟的功能,例如处理数据的功能。 PHPUnit 是这种测试的糟糕选择吗?

提前感谢您的宝贵时间。我意识到我所做的一些假设可能不正确,因为我显然不是这方面的专家;欢迎和赞赏更正。

最佳答案

工厂就像new 关键字。您是否测试了 new 关键字?不,你测试你是否可以构造一个类。但这独立于工厂本身和单元的一部分,因此已经成为单元测试的一部分。

2) 称为集成测试。您也可以使用 PHPUnit 做到这一点。


Edit - As there was some discussion in comments:

就单元测试而言,您可以对您的工厂进行单元测试,以确定它的用途:返回一个具体类型、一个类型或任何类型。

这没有什么不对,但是通常没有必要,因为返回类型的构造函数已经在单元测试中,而且测试真的很简单,只是数据检查,闻起来像集成测试。如果无法提供依赖项,那些将工厂类型作为依赖项(并且也在进行单元测试)的类型也会导致编译/执行失败。因此,工厂的所有用途都已经过测试,甚至来自双方。如果工厂没有被消耗,那么你就不需要测试它了。

我建议您创建一次纯 TDD 风格的工厂,以便预先制定使用,然后您就会对此有所了解。您可能想要测试工厂类的其他方面,但这可能更属于集成而不是单元测试。

而且我不想给人留下这样的印象,即您的其他单元实际上应该对工厂创建方法进行硬编码调用,而不是注入(inject)依赖项。因为你不应该在你的单元中使用 new,你也不应该在其中使用 Factory::create。与 new 类似,类名 (Factory) 是硬编码的,而不是注入(inject)的。那么它是一个隐藏的依赖项。但是不应该隐藏依赖关系;但可见。

关于php - 使用嵌套依赖项和工厂类执行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10128780/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com