在单元测试中,之所以选择使用 Spy(间谍) 而不是直接使用真实对象,主要有三个核心原因:

1. 能够“监视”行为(Verify)

如果你直接使用真实对象,你只能看到结果,却无法确认内部发生了什么。

  • 真实对象:你调用 list.add("Hello"),它只会告诉你是成功了,但它不会记录你是否真的调用了这个方法。
  • Spy:它像一个“监控摄像头”。除了执行正常逻辑外,它还记录了:
    • 这个方法被调用了几次?
    • 传入的参数是什么?
    • 调用顺序是否正确?

例子: 如果你在写一个账户转账系统,使用真实对象你只能查到余额变了。但使用 Spy,你可以验证代码内部确实调用了“记录日志”这个方法,而不仅仅是修改了余额。

2. 能够“劫持”关键逻辑(Stubbing)

有时候,一个真实对象太“笨重”了,或者包含了我们不想触发的逻辑。

  • 真实对象的问题:如果这个对象内部包含了一些“危险”行为,比如:连接真实的数据库、发送真实的邮件请求、调用收费的第三方 API。你在运行单元测试时,如果直接用它,测试环境会变得不可控,甚至产生费用或脏数据。
  • Spy 的优势:你可以只对它其中的某一个方法进行“劫持”(Stubbing),让它返回你指定的值,而让其余的方法保持真实的行为。

你可以理解为: 它是“真实对象”的改良版。你保留了它 90% 的真实逻辑,但通过 Spy 屏蔽了那 10% 会导致测试失败或不稳定的危险逻辑。

3. 可控性与隔离性

单元测试的核心是“隔离”。我们希望测试的是“当前的逻辑”,而不是整个系统的运行情况。

  • 如果我们直接使用真实对象(比如真实连接了数据库),一旦数据库挂了,你的测试就会失败,但这并不代表你的代码写错了。这种测试叫“集成测试”,耗时且不稳定。
  • 使用 Spy,你可以把复杂的外部依赖转化为可控的操作,确保你的测试只针对你的代码逻辑,无论外部环境如何,测试结果永远是确定且快速的。

总结对比表

特性 直接使用真实对象 使用 Spy (间谍)
功能执行 运行真实逻辑 运行真实逻辑
可否调用验证 (Verify) 不能
可否部分修改行为 (Stub) 不能
是否依赖外部环境 高(容易报错) 低(更加稳定)

更多推荐