Php 使用嵌套的依赖项和工厂类执行单元测试
我是单元测试和PHPUnit的新手,但我最近读了很多关于设计模式和独立测试的书,我决定重构我正在开发的应用程序,以摆脱静态类、单例、硬编码依赖项和在全局范围内定义的任何其他内容,希望使其“可测试”这对未来的曼坦来说不是一件痛苦的事,因为这是一个长期的项目 到目前为止,我相信我理解了单元测试背后的理论,但我想知道,在一个将处理对象嵌套依赖关系的任务委托给工厂的场景中,应该如何对所述工厂进行单元测试,还是测试它只是多余的?测试依赖项的“链”是否同步工作的最佳方法是什么 让我举例说明这些问题。假设您有以下“遗留”代码:Php 使用嵌套的依赖项和工厂类执行单元测试,php,unit-testing,design-patterns,phpunit,Php,Unit Testing,Design Patterns,Phpunit,我是单元测试和PHPUnit的新手,但我最近读了很多关于设计模式和独立测试的书,我决定重构我正在开发的应用程序,以摆脱静态类、单例、硬编码依赖项和在全局范围内定义的任何其他内容,希望使其“可测试”这对未来的曼坦来说不是一件痛苦的事,因为这是一个长期的项目 到目前为止,我相信我理解了单元测试背后的理论,但我想知道,在一个将处理对象嵌套依赖关系的任务委托给工厂的场景中,应该如何对所述工厂进行单元测试,还是测试它只是多余的?测试依赖项的“链”是否同步工作的最佳方法是什么 让我举例说明这些问题。假设您有
阶级之家{
受保护的材料;
受保护的门;
受保护的$旋钮;
公共函数构造(){
$this->door=新门();
$this->knob=$this->door->getKnob();
$this->material=“stone”;
echo“House material:”.this->material.PHP_EOL.
;
回显“门材质:”.$this->Door->getMaterial().PHP_EOL.“
”;
回显“旋钮材质:”.$this->Knob->getMaterial().PHP_EOL.“
”;
}
}
班级门{
受保护的材料;
受保护的$旋钮;
公共函数构造(){
$this->knob=新的knob();
$this->material=“wood”;
}
公共函数getKnob(){
返回$this->knob;
}
公共函数getMaterial(){
返回$this->material;
}
}
类旋钮{
受保护的材料;
公共函数构造(){
$this->material=“metal”;
}
公共函数getMaterial(){
返回$this->material;
}
}
$house=新房子();
这(据我所知)不利于单元测试,因此我们用DI+a工厂类替换硬编码依赖项:
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();
阶级之家{
受保护的材料;
受保护的门;
受保护的$旋钮;
公共功能建筑($door){
$this->door=$door;
$this->knob=$this->door->getKnob();
$this->material=“stone”;
echo“House material:”.this->material.PHP_EOL.
;
回显“门材质:”.$this->Door->getMaterial().PHP_EOL.“
”;
回显“旋钮材质:”.$this->Knob->getMaterial().PHP_EOL.“
”;
}
}
班级门{
受保护的材料;
受保护的$旋钮;
公共功能结构(旋钮){
$this->knob=$knob;
$this->material=“wood”;
}
公共函数getKnob(){
返回$this->knob;
}
公共函数getMaterial(){
返回$this->material;
}
}
类旋钮{
受保护的材料;
公共函数构造(){
$this->material=“metal”;
}
公共函数getMaterial(){
返回$this->material;
}
}
高级住宅工厂{
公共函数create(){
$旋钮=新旋钮();
$door=新门(旋钮);
$house=新房子($door);
归还$house;
}
}
$houseFactory=新的houseFactory();
$house=$houseFactory->create();
现在(而且,据我所知)房子、门和把手都可以通过模拟依赖项进行单元测试。但是:
1) 现在工厂怎么办
我们应该:
- 不要测试它,因为它还没有任何值得测试的应用程序逻辑,工厂通常都是这样。假设如果房屋、门和把手的独立测试通过,则工厂应无问题
- 以某种方式重构工厂,即使用类中的函数获取每个实例,这样可以通过PHPUnit重写这些函数以返回模拟对象,以防类中有一些额外的逻辑在将来可能需要进行一些测试
提前感谢您抽出时间。我意识到我所做的一些假设可能不正确,因为我显然不是这方面的专家;欢迎并感谢您的更正 工厂就像
new
关键字一样。您是否测试new
关键字?不,您测试是否可以构造类。但这独立于工厂本身和单元的一部分,因此已经是单元测试的一部分
2) 被称为集成测试。你也可以用PHPUnit来做
编辑-评论中有一些讨论: 就单元测试而言,您可以对工厂进行单元测试,以实现它的目的:返回具体类型、类型或任何类型 这并没有什么错,但通常并没有必要,因为返回类型的构造函数已经在进行单元测试,而且该测试非常简单,只是数据检查,闻起来像是集成测试。此外,如果不能提供依赖项,则那些将工厂中的该类型作为依赖项的类型(以及正在进行单元测试的类型)将导致编译/执行失败。因此,工厂的一切都已经过测试,即使是从双方。如果工厂没有被消耗,那么你就不需要测试它 我建议你创建一个纯TDD风格的工厂,以便公关
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();