【发布时间】:2010-03-03 09:55:41
【问题描述】:
人们常说接口使模拟和单元测试变得更容易。接口对此有何帮助?
【问题讨论】:
-
查看动态模拟的解释:stackoverflow.com/questions/1972831/…
标签: unit-testing mocking
人们常说接口使模拟和单元测试变得更容易。接口对此有何帮助?
【问题讨论】:
标签: unit-testing mocking
接口的本质是提供许多实现,从而启用模拟。
特别是在集成测试中,您可以提供依赖系统模型的版本(例如 Web 服务)。 您可以提供一个最简单的接口实现,而不是实际调用依赖系统甚至模块,或复杂且难以实例化的类型将提供单元测试正确完成所需的结果。
除此之外,当你在单元测试中使用时,一个实际的依赖类型(称之为BigGraph)隐藏了一个复杂的对象模型,其实你就是做集成测试而不是单元测试。 如果任何依赖类型存在错误(BigGraph),您的测试很容易中断,而不是您正在测试的类型,因此不是单元测试。使用模型可以降低发生这种情况的风险。
我见过许多持续集成系统针对一个错误显示数十个错误,而实际上应该显示一个或最多两个错误,这都是因为对象模型太复杂,以及单元测试编写不正确——没有使用模拟-ups。
如今的 mocking 框架比过去更复杂(字节码修改等),因此有时并不总是需要接口甚至虚拟方法,但无脑接口可以实现它们。
如果您的对象模型过于复杂和混乱(例如,您的界面严重依赖其他类型/界面), 界面将无济于事;那么实现/模拟这一切是件痛苦的事。
【讨论】:
如果你有一个类,你可以有很多依赖,比如
疯狂的构造函数(很多参数,或者它需要一些其他类,需要第三个类,而第四个类需要有效的数据库连接)
要以任何方式使用该类,您必须正确初始化它(例如,您必须向它传递一些其他必须有效的对象)
类有一个状态。由于某种原因,此状态在测试期间可能会发生变化。
该类可能具有或使用静态字段
在您的许多测试中,这些依赖项通常是无关紧要的,您宁愿不必处理它们。
使用接口,您可以创建一个简单的模拟类,它只实现您需要的几个方法。大多数模拟框架都内置了对此的支持。这里的“实现”通常意味着“返回一个固定值”。这样,您可以快速构建被测类所需的环境。
例如,如果您的类需要从数据库中读取记录,您可以改为模拟一个只返回行的ResultSet。因此,您不必拥有一个真正的数据库,您不需要创建连接(这很慢并且可能由于多种原因而失败),您不必关心数据库中的数据(所以您不需要'不必删除/删除表并用测试数据再次填充它们)等。
【讨论】:
如果您有多个对象具有不同的实现,但向外部提供相同的方法,共享相同的接口,您可以编写一个单元测试并针对所有实现运行它。
如果您有一个类说将内容发布到 Web 后端,然后检索一个有问题的答案单元测试,因为失败的 Internet 连接可能会让您的测试失败。因此,您为此类定义一个接口,然后您可以编写第二个实现来记录应该发送的内容并提供正确的答案。通过这种方式,您可以在测试运行期间测试使用来自后端的答案的类,而无需依赖 Internet 连接。
【讨论】:
如果你没有绑定到具体的实现,你可以在接口后面切换行为。
如果您的类实现接口,则可以模拟行为。
例如,您不需要向数据库中的所有客户发送垃圾邮件来测试您的邮件通知算法是否有效。您将为IMailSender 界面创建一个模拟,并且只计算发送的电子邮件数量。然后,您将测试在单个电子邮件地址上实际发送电子邮件的实际实现,并且您知道整个通知过程有效。
在这个特定示例中,测试使用了一个模拟实现 IMailSender,它只计算发送的电子邮件,而您的实际生产代码将使用一个实现 IMailSender 的实现,它实际上通过 SMTP 服务器发送电子邮件。
【讨论】:
在测试跨端口的行为时,为适配器提供接口将有助于在测试期间注入不同的实现(即test double)。
例如,调用数据库或 Web 服务的 DAO 可以替换为返回存根数据的实现。
【讨论】: