当你想在受测试的某一类别和某个特定接口之间测试互动时,磁物体是有用的。
例如,我们要测试以下方法:send Invitations(mailServermailServer) callsmailServer.createMessage(
,准确无误地一次;mailerver.sendMessage(m)
,而且没有其他方法在接口上发出。 这就是我们能够使用模拟物体的时候。
使用模拟物体,而不是通过真实的<代码> 邮件ServerImpl,或测试<代码> 试验邮件Server<> 代码”,我们可以通过模拟执行<密码>。 在我们通过一个 mo形码(/code>之前,我们“train”,这样它就知道需要什么样的方法,以及返回的价值。 最后, mo子声称,所有预期方法都按预期使用。
这在理论上是好的,但也有一些下滑。
Mock shortcomings
如果你有一个模拟框架,你就被诱惑使用模拟物体every time,你需要通过试样与舱面连接。 这样,你就可以结束<斯特隆>的交流,即使没有必要。 不幸的是,对互动的不必要(偶然)测试是坏的,因为当时,你重新测试一项具体要求以某种方式执行,而不是因为执行产生必要的结果。
这里就是一个假编码的例子。 让我们ve立一个<条码>。
// the correct way of testing
testSort() {
testList = [1, 7, 3, 8, 2]
MySorter.sort(testList)
assert testList equals [1, 2, 3, 7, 8]
}
// incorrect, testing implementation
testSort() {
testList = [1, 7, 3, 8, 2]
MySorter.sort(testList)
assert that compare(1, 2) was called once
assert that compare(1, 3) was not called
assert that compare(2, 3) was called once
....
}
(在这个例子中,我们假设,它不是一种特殊的分类算法,例如快速算法,我们希望测试;在这种情况下,后一种检验实际上有效。)
在这样一个极端的例子中,它显然说明为什么后者是错误的。 当我们改变<代码>MySorter的实施时,第一种检验方法非常有助于确保我们仍然正确行事,这是整个测试点,使我们能够安全地改变守则。 另一方面,后一项检验标准always断裂,而且它非常有害;它阻碍重温。
Mocks as stubs
磁场框架往往允许使用较少的严格做法,在这种情况下,我们不必确切地说明应当使用多少方法和预期的参数;它们允许制造用作stubs。
让我们使用一种方法<代码>后邀请(PdfFormatter pdfFormatter, 邮报ServermailServer),我们希望测试。 <代码>PdfFormatter物体可用于制作邀请书。 试验:
testInvitations() {
// train as stub
pdfFormatter = create mock of PdfFormatter
let pdfFormatter.getCanvasWidth() returns 100
let pdfFormatter.getCanvasHeight() returns 300
let pdfFormatter.addText(x, y, text) returns true
let pdfFormatter.drawLine(line) does nothing
// train as mock
mailServer = create mock of MailServer
expect mailServer.sendMail() called exactly once
// do the test
sendInvitations(pdfFormatter, mailServer)
assert that all pdfFormatter expectations are met
assert that all mailServer expectations are met
}
在这个例子中,我们并不真正关心<代码>。 PdfFormatter Object, so we only training it to quietly receive any calls and re some sensible canated Return Value for all methods send Invitation (
when are to calls at this point. 我们如何确切提出这一培训方法清单? 我们只是进行了试验,在试验通过之前不断补充方法。 我们发出这样的通知,即我们训练了 st,以回应一种方法,而没有它为什么需要称之为这种方法,我们只是增加了试验所抱怨的一切。 我们高兴的是,测试通行证。
但后来,当我们修改<条码>后邀请书(条码>)或使用<条码>后邀请书(条码>)的其他一些类别,以创造更多的活力? 我们的测试突然失败,因为现在需要采用更多的<代码>PdfFormatter的方法,而且我们没有培训我们期望这些方法。 通常,它不仅是一种在类似情况下失败的测试,而且还会受到直接或间接使用<代码>后邀请(方法的任何测试。 我们必须通过增加更多的培训来确定所有这些试验。 我们还注意到,我们无法消除不再需要的方法,因为我们不知道其中哪些办法不需要。 同样,这妨碍了令人振奋。
而且,试验的可读性也受到可怕的影响,在那里,由于我们想要,但因为我们不得不这样做,我们没有书写文字;它不是我们谁想要这个法典。 使用模拟物体的试验非常复杂,往往难以阅读。 测试应有助于读者了解,如何使用测试中的类别,因此,这些类别应当简单明了。 如果他们不能读懂,任何人都不会维持;事实上,删除他们比保持他们更容易。
如何确定这一点? 容易:
- Try using real classes instead of mocks whenever possible. Use the real
PdfFormatterImpl
. If it s not possible, change the real classes to make it possible. Not being able to use a class in tests usually points to some problems with the class. Fixing the problems is a win-win situation - you fixed the class and you have a simpler test. On the other hand, not fixing it and using mocks is a no-win situation - you didn t fix the real class and you have more complex, less readable tests that hinder further refactorings.
- Try creating a simple test implementation of the interface instead of mocking it in each test, and use this test class in all your tests. Create
TestPdfFormatter
that does nothing. That way you can change it once for all tests and your tests are not cluttered with lengthy setups where you train your stubs.
几乎所有物体都有其使用,但如不谨慎使用,they往往鼓励不良做法、测试执行细节、阻碍重构和难以阅读和难以维持。
关于摩擦缺点的更多详情,另见。 ock:短程和使用案例。