来自维基百科(强调我的,内部参考已删除):
在“单元测试的艺术”一书中,mock 被描述为一个假对象,它通过验证是否发生了与对象的交互来帮助确定测试是失败还是通过。
在我看来,模拟正在测试实现。具体来说,他们测试实现与特定对象交互的方式。
我是否正确解释了这一点?模拟是故意破坏“测试接口,而不是实现”的口头禅吗?或者,模拟测试是否在单元测试以外的级别进行测试?
来自维基百科(强调我的,内部参考已删除):
在“单元测试的艺术”一书中,mock 被描述为一个假对象,它通过验证是否发生了与对象的交互来帮助确定测试是失败还是通过。
在我看来,模拟正在测试实现。具体来说,他们测试实现与特定对象交互的方式。
我是否正确解释了这一点?模拟是故意破坏“测试接口,而不是实现”的口头禅吗?或者,模拟测试是否在单元测试以外的级别进行测试?
正确,模拟不遵循“测试接口,而不是实现”的古典主义口号。模拟使用行为验证而不是状态验证。
来自http://martinfowler.com/articles/mocksArentStubs.html:
模拟使用行为验证。
因此,Mockist 测试与方法的实现更加耦合。改变调用协作者的性质通常会导致 mockist 测试中断。
这种耦合导致了几个问题。最重要的是对测试驱动开发的影响。使用 mockist 测试,编写测试会让您考虑行为的实现——事实上,mockist 测试人员认为这是一个优势。然而,古典主义者认为重要的是只考虑从外部接口发生的事情,并在完成编写测试之后才考虑实现。
在我看来,模拟正在测试实现。具体来说,他们测试实现与特定对象交互的方式。
100% 正确。但是,这仍然是单元测试,只是从不同的角度来看。假设您有一个方法应该使用某种MathsService
. 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
方法。
希望有帮助...