如何单元测试实例创建?
问题描述:
我有一个Carpenter
类,它使用Lathe
和Wood
对象来工作。如何单元测试实例创建?
class Carpenter
{
function Work()
{
$tool = new Lathe();
$material = new Wood();
$tool->Apply($material);
}
}
Lathe
取决于称为Material
在接口上,这样我就可以很容易地进行单元测试Lathe
给它一个假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
取决于具体的类Lathe
和Wood
,因为它创建它们的实例。这意味着,就目前而言,我不能单元测试Work()
方法,而不会不经意地将Lathe
和Wood
进行测试。
我应该如何将我的设计更改为单元测试Carpenter
?
答
有几个不同的方向,你可以采取这里:
- 使用构造器注入,只需注入工具和材料的情况下,进入木匠。
- 如果由于某种原因注入实例不起作用(可能是因为您需要为每次调用Work方法创建新实例),则可以注入抽象工厂。
- 您也可以使用ctford描述的工厂方法方法,但这需要您创建特定于测试的覆盖以便能够进行单元测试,尽管这是一个完全有效的事情,但它只是更多的工作而已在很多情况下,其他方案更好,更灵活。
答
关键是要在对象使用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()
方法也可以单元测试单独。
Michael Feathers会调用getTool()
和getMaterial()
方法“接缝”,因为它们是可以在不改变周围代码的情况下插入假货的点。
有趣。但是,如果你注入一个抽象工厂,是不是只是将单元测试问题转移到创建抽象工厂并将其注入到Carpenter中的方法中?谢谢你的帮助。 – ctford 2009-12-15 09:42:12
不,因为使用抽象工厂将对象创建从行为中分离出来,这意味着您可以对每个对象进行单元测试。 – 2009-12-15 11:43:41
您可以对抽象工厂本身进行单元测试,但创建抽象工厂并将其提供给木匠的方法与原始问题中的问题相同。你必须找出一种方法将测试抽象工厂注入到该方法中(并且我认为抽象工厂工厂不会成为下一步:) ......对吗? – ctford 2009-12-15 13:30:39