假设我们有在A类中测试的方法,它从类B调用方法。为了测试它,我们为B创建了mock,然后验证它是否被调用。验证(...)是否足以进行单元测试或者我需要断言测试方法的实际结果? 以下是澄清我的担忧的简化示例:
public class StringWriterATest {
StringWriterB b = mock(StringWriterB.class);
@Test
public void stringWriterATest() {
StringBuffer sb = new StringBuffer();
StringWriterA a = new StringWriterA();
a.stringWriterB=b;
a.append(sb);
ArgumentCaptor<StringBuffer> argument = ArgumentCaptor.forClass(StringBuffer.class);
verify(b).append(argument.capture());
assertEquals("StringWriterA", ((StringBuffer)argument.getValue()).toString());
//do we really need this or above is enough for proper unit test of method a.append(sb);
//assertEquals("StringWriterA_StringWriterB", sb);
}
}
public class StringWriterA {
public StringWriterB stringWriterB;
public void append(StringBuffer sb) {
sb.append("StringWriterA");
stringWriterB.append(sb);
}
}
class StringWriterB {
public void append(StringBuffer sb) {
sb.append("StringWriterB");
}
}
此致 最大
答案 0 :(得分:1)
永远不需要模拟返回值并同时验证对象。
考虑一下:
StringWriterA
是受测试的课程。因此,您肯定希望使用断言来验证此类的行为。为此,您模拟了一个依赖项StringWriterB
。
您 想要在StringWriterB
的测试中测试StringWriterA
,因此您的测试中StringWriterB
个互动的任何断言都是错误的地方。
您必须假设StringWriterB
的行为符合预期。您要么正确验证StringWriterA
正确调用StringWriterB
(使用verify()
)或您要模拟其预期行为并模拟返回值。
如果你模拟,那么验证是隐式的,因为如果没有调用该方法,则不会返回模拟的返回值。
在您的情况下,StringWriterA.append()
不会返回任何值,因此只能进行验证。 StringWriterB.append()
也可以运行,应该在自己的stringWriterBTest
中进行类似的验证测试。
注意:通过测试明确表示很高兴。由于测试方法永远不会在框架之外调用,因此永远不需要输出它们,因此您可以拥有比生产代码方法更长的方法名称。一个很好的约定是:
<underTest>Should<Expected>[When]<Condition>()
即
stringWriterAShouldAppendConstantAndDelegateToStringWriterB()
stringWriterAShouldThrowNullPointerExceptionWhenNullArgument()
如果您的构建中存在测试失败(持续集成),那么您不必寻找出错的方法,方法名称会出现失败,您可以阅读它以确切知道必须修复哪些行为
答案 1 :(得分:0)
在您的示例中,StringWriterB
不存储任何状态,append
方法可能很容易变为静态。在那种情况下,调用纯粹是副作用,不需要进行测试。
但是,我怀疑你的真实代码要复杂得多。如果有另一个对象访问StringWriterB
,那么你可能想要模拟它以防万一有意外的调用。如果您希望将来扩展B,您可能还想添加B的验证 - 可能存储来自追加调用的状态并具有访问者。
要考虑的一件事是对StringWriterA.append()
的调用的目的是什么。如果它的工作是附加字符串StringWriterAStringWriterB
那么那就是你应该测试的东西,并且不需要模拟。 StringWriterA
如何完成这项任务并不重要。但是,如果它的部分工作是调用StringWriterB.append()
方法,那么除非你想在A的测试中测试StringWriterB
,否则可能需要模拟。
我的经验法则WRT模拟是使用真实物体,直到我没有直接测试的物体的线路变得太毛或太脆。如果我觉得我的测试的很大一部分是在实际测试其他对象,那么嘲笑将是一个好主意。
答案 2 :(得分:0)
首先尝试向某人展示您编写的测试。在我看来很难读。 你无法真正告诉它你正在测试什么行为。您可以在此处找到如何使其更具可读性的简要介绍How to Write Clean, Testable Code 。 使用参数捕获者也是一种气味。可以在tdd blog上找到一些如何避免它的示例。
要回答您的问题,verify
用于验证类之间的交互。 用于推动代码设计。结果(如果需要)应由开头的when
或given
指定你的考试。
有关如何使用模拟驱动设计的更多信息(when
,given
,verify
,...)以及模拟与存根的不同之处可在此处找到:{{3 }}。该示例使用JMock而不是Mockito来定义模拟,但它非常相似(它是关于概念,而不是您使用的实现和库的细节)。