mock objects就是特洛伊木马,但是它们并没有恶意。mocks 从内部替代真正的对象,并且调用类并不会意识到这一点。mocks可以访问有关类的内部信息,使得它们更加有用。在当前的示例中,我们仅仅使用它们来模拟真实的行为,但我们并没有挖掘它们能够提供的所有信息。
可以把mocks用作探测器,监视待测试对象所调用的方法。让我们以这个HTTP 连接为例。我们可以监视的一个非常有趣的调用就是Inputstream的close方法。迄今为止我们没有对 Inputstream使用一个mock object,但是我们可以很容易地创建一个,并且提供一个 verify方法来确认 close已经被调用。然后我们可以在测试的最后调用verify方法来验证所有应该调用的方法是否都已经被调用了(如代码7.13所示)。我们可能也会想要验证close是否只被调用了一次,如果它被调用多次或者根本没有被调用,那么就抛出异常。这些验证通常被称为预期(expectation)。
DEFINITION 预期( expectation)——当我们谈论mock objects 的时候,预期就是内嵌在mock
里面的一个特性,用来验证调用此mock的外部类是否具有正确的行为。例如,一个数据库连接mock能够验证,在任何使用该mock的测试中,这个连接中的close方法都被调用了一次。
代码7.13中展示了一个关于预期((expectation)的示例。
在 MockInputstream类中,close的预期很简单:我们希望它总是被调用一次。但是在大多数情况下,closecount的预期都依赖于待测试代码。一个mock 通常会有一个类似setExpected-Closecalls的方法,这样测试就可以告诉mock预期会发生什么。
接下来让我们修改TestWebclient.testGetcontentok的测试方法,以便使用新的MockInputStream:
在之前的测试中使用的是真正的ByteArrayInputstream,而现在我们使用的是MockInputStream。请注意,我们在测试的结尾处调用了MockInputStream 的验证方法,以确认是否满足了我们所有的预期。运行测试的结果如图7.4所示。
这个测试会失败并弹出消息“close () should have been called once and once only"。这是为什么?因为我们没有关闭webclient.getContent方法中的输人流。如果我们关闭了两次或者更多次,同样的问题还是会出现,这是因为测试检查的是被调用一次并且仅仅一次。
让我们修改一下待测试代码(参见代码7.14)。
图7.4 在新的close预期下运行 TestWebClient
现在我们就可以看到漂亮的绿色进度条了,如图7.5所示。
对于预期,还有一些其他简单方便的用法。例如,假如你有一个组件管理器,在组件生命周期内调用不同的方法,那么你可能会预期这些方法是按照一定的顺序被调用的。或者,你可能会期望某个指定的值作为参数传递给mock。一般可以认为,除了能够在测试中提供你想要的行为,mock同样可以就其使用情况提供有用的反馈。
图7.5 运行的 WebClient关闭了输入流
下一章节将会展示使用一些最流行的开源mock框架的使用——它们功能强大,完全能够满足我们的需求,并且我们不需要从一开始就实现mock。