嘲讽打破了“测试界面,而不是实施”的咒语?

时间:2013-09-15 04:39:25

标签: unit-testing testing mocking

来自维基百科(强调我的,删除了内部参考资料):

  

在书"单元测试的艺术"模拟被描述为一个虚假对象,可帮助确定测试是否失败或通过验证与对象的交互是否发生

在我看来,模拟正在测试实现。具体来说,他们测试实现与特定对象交互的方式。

我是否正确地解释了这一点?嘲笑是故意破坏"测试界面,而不是实施"口头禅?或者,模拟测试是否在单元测试之外进行测试?

2 个答案:

答案 0 :(得分:5)

正确,嘲笑不遵循“测试界面,而不是实现”的古典主义口头禅。模拟使用行为验证而不是状态验证。

来自http://martinfowler.com/articles/mocksArentStubs.html

  

模拟使用行为验证。

     因此,模拟测试更多地与方法的实现相关联。改变对协作者的调用性质通常会导致模拟测试中断。

     

这种耦合导致了一些问题。最重要的是对测试驱动开发的影响。通过mockist测试,编写测试可以让你思考行为的实现 - 事实上,模仿者测试者认为这是一个优势。然而,古典主义者认为,只考虑从外部界面发生的事情并在完成测试之后考虑实施的所有考虑因素是很重要的。

答案 1 :(得分:2)

  

在我看来,模拟正在测试实现。   具体来说,他们测试实现与特定对象交互的方式。

100%正确。但是,这仍然是单元测试,只是从不同的角度来看。 假设您有一种方法可以使用某种MathsService对2个数字执行函数。为MathsService课程提供了Calculator,以便为计算器进行数学计算。

让我们假装MathsService有一个方法,PerformFunction(int x, int y)应该只是return x+y

像这样测试:(以下所有=伪代码,为清楚起见省略了某些位)

var svc = new MathsService();
var sut = new Calculator(svc);
int expected = 3;
int actual = sut.Calculate(1,2);
Assert.AreEqual(expected,actual,"Doh!");

这是单位Calculator.Calculate()黑匣子测试。您的测试不知道或关心答案是如何得出的。这很重要,因为它可以让您对测试的正确性有一定的信心。

但是,请考虑Calculator.Calculate的这种实现:

public int Calculate()
{
    return 4;
}

像这样测试:

var svc = new Mock<IMathsService>(); //did I mention there was an interface? There's an interface...
svc.Setup(s=>PerformCalculation(1,2)).Returns(3);
var sut new Calculator(svc.Object);
sut.Calculate(1,2);
svc.Verify(s=>PerformCalculation(1,2),Times.Once,"Calculate() didn't invoke PerformFunction");

白框测试没有告诉您任何关于PerformFunction方法的正确性,但确实无论结果如何,Calculator都会将x和y传递给IAdditionService.PerformCalculation方法,这就是您希望它做的。

您当然可以编写其他测试来验证PerformCalculation测试的结果是否在不修改调用者的情况下传回。

有了这些知识,如果您的第一次单元测试失败,您可以高度自信地跳到MathService课程中寻找问题因为您知道问题可能不是Calculator.Calculate方法。

希望有帮助...