Unit testing 如何对实例创建进行单元测试?

Unit testing 如何对实例创建进行单元测试?,unit-testing,dependency-injection,Unit Testing,Dependency Injection,我有一个Carpenter类,它使用车床和木材对象来完成工作 class Carpenter { function Work() { $tool = new Lathe(); $material = new Wood(); $tool->Apply($material); } } 车床依赖于名为材料的接口,因此我可以通过在单元测试中给它一个假的材料来轻松地对车床进行单元测试Wood不依赖于任何东西,因此也可以轻松测试

我有一个
Carpenter
类,它使用
车床和
木材
对象来完成工作

class Carpenter
{
    function Work()
    {
        $tool = new Lathe();
        $material = new Wood();
        $tool->Apply($material);
    }
}
车床
依赖于名为
材料
的接口,因此我可以通过在单元测试中给它一个假的
材料
来轻松地对
车床
进行单元测试
Wood
不依赖于任何东西,因此也可以轻松测试

interface Material {
    // Various methods...
}

interface Tool {
    function Apply(Material $m);
}

class Wood implements Material  {
    // Implementations of Material methods
}

class Lathe {
    function Apply(Material $m) {
        // Do processing
    }
}
但是,
Carpenter
取决于具体的类
latch
Wood
,因为它必须创建它们的实例。这意味着,就目前的情况而言,我无法对
Work()
方法进行单元测试,除非无意中对
车床
Wood
进行测试


我应该如何将我的设计更改为单元测试
Carpenter

关键是在
Carpenter
中分离对象创建和对象使用

class Carpenter
{
    function getTool() {
        return new Lathe();
    }
    function getMaterial() {
        return new Wood();
    }
    function Work()
    {
        $tool = getTool();
        $material = getMaterial();
        $tool->Apply($material);
    }
}
这样,您就可以重写
TestCarpenter
类中的
getTool()
getMaterial()
方法,并注入您自己的
Material
Tool
赝品

getTool()
getMaterial()
方法也可以单独进行单元测试


getTool()
getMaterial()
方法称为“seams”,因为它们是可以在不改变周围代码的情况下插入赝品的点。

这里有两个不同的方向:

  • 使用构造函数注入并简单地将工具和材质实例注入到carpenter中
  • 如果由于某种原因注入实例不起作用(可能是因为您需要为每次调用work方法创建新实例),则可以注入抽象工厂
  • 您也可以使用ctford描述的工厂方法方法,但这也要求您创建特定于测试的覆盖,以便能够进行单元测试,虽然这是一件完全有效的事情,但只需要做更多的工作,在许多情况下,其他替代方法更好、更灵活

很有趣。但是,如果你注入一个抽象工厂,你不就是把单元测试问题转移到创建抽象工厂并将其注入Carpenter的方法上吗?谢谢您的帮助。不,因为使用抽象工厂将对象创建与行为分离,这意味着您可以在isotaion中对每个对象进行单元测试。您可以对抽象工厂本身进行单元测试,但创建抽象工厂然后将其提供给木匠的方法将遇到与原始问题相同的问题。您必须找到一种方法,将测试抽象工厂注入到该方法中(我不认为抽象工厂是可行的:)。。。对吗?抽象工厂或任何其他依赖项可以(通常应该)通过构造函数注入(构造函数注入)。来电者有责任提供正确的工厂信息。