Faking、Mocking和Stubbing之间的区别

2025/05/06

1. 概述

在测试软件应用程序或产品时,我们可能会创建一些不真实的对象或组件,我们需要它们来模拟对日常场景和极端情况的响应,我们通常将此过程称为创建Fakes或测试替身。当我们采用测试库或框架来自动化软件测试流程时,我们也可以参考Mock或Stub。

在本教程中,我们将了解如何使用虚构数据,例如Fake、Mock和Stub。

2. 为什么我们需要Fake、Mock和Stub

测试有很多种类型,例如单元测试或集成测试。但是,我们可以将测试序列简化为以下几个步骤:

每当我们构建测试时,都应该根据业务需求创建场景。然后,我们可以模拟所有不同的情况,并断言我们根据测试的实际结果获得了预期的结果。常见的方法是使用TDD来测试代码覆盖率,或使用BDD来向用户解释应用程序的行为。

然而,我们需要为测试套件提供一些输入,获取真实数据可能非常耗时。此外,还可能存在一些问题,例如,为测试实例中不可用的服务添加覆盖范围。

让我们看看我们可以在测试环境中应用的数据类型和技术。

3. Fake

我们可以将任何与生产环境中找不到的数据、对象或实现归类为Fake。因此,无论我们使用何种具体技术,Fake都是我们用来模拟实际应用中行为的东西。它通常只是一个类或模块的副本,其中包含预先计算好的响应

3.1 伪造对象

通常,我们可以将Fake视为具有有限功能的类对象的副本或克隆。例如,假设我们有一个UserRepository类实现了一个接口:

我们可能需要我们的Repository返回一个虚假值,在这种情况下,Fake将提供自定义响应,例如,一个静态值或内存值,而不是实际的计算结果。

3.2 Fake缺点

这种方法也有一些缺点,例如,我们不能直接伪造接口。因此,我们每次都需要实现一个伪类。此外,我们需要将对象保存到内存映射中,以便提前测试。这种方法可能不够灵活,让我们看看编程语言和我们使用的库如何使Mock或Stub成为Fake更合适的替代品。

4. Mock

我们将Mock称为代表现实的对象,尽管其功能有限。此外,在开始测试之前,我们会对Mock设定预期,并指示它在发生某些事情时保持确定性。这样,我们就可以枚举应用程序特定流程中遇到的所有不同状态,并构建一系列测试。

我们可以将Mock视为一个木偶或牵线木偶,我们可以操纵它在某些条件下按照我们需要的方式行事

4.1 Mock对象

我们可以Mock一个类或接口Mock对象的实际实现不会被调用,即使Mock接口尚未实现也没关系。请注意,Mock类的属性将返回默认值。例如,对于字符串或列表,我们将返回null或false作为布尔值。

4.2 Mock和验证交互

让我们举一个Mock用法的例子,假设我们有AuthenticationService,该服务使用依赖倒置依赖于UserRepository,让我们看看如何Mock Repository:

Mock操作默认会遍历对象的依赖注入层级,如果没有特别指定,我们将获得一个UserRepository的Mock实例和一个数据库连接Mock。需要注意的是,对于findUserByName()方法,其返回值默认为空User,但我们可以添加自定义响应。

对于单元测试和隔离类的不同工作单元而言,Mock可以节省我们创建数据库连接的时间,我们甚至可以在无需实际实现的情况下进行接口Mock。因此,无论数据库实现如何,我们都可以断言业务逻辑能够正常工作。

最后,Mock的另一个相关特性是可以验证是否发生所需的交互

让我们用一张图来概括这一切:

测试身份验证服务

此流程描述了服务如何工作,无论其实现方式如何。它应该涵盖所有可能的情况,并将转换为具体的测试。如果逻辑发生变化,我们也预期测试会相应地失败。

4.3 Mock实践

在实际应用中,Mock由特定的库或框架管理。开发人员通常依赖库来将其插入到应用程序中,这些库将构建测试基础架构并管理我们想要Mock的对象的生命周期。开发人员负责将业务需求转化为测试覆盖率,让我们来看看一些流行的Mock库/框架:

编程语言 库/框架
Java Mockito是一个流行的库,Spock是一个使用given/when/then语法的测试框架
.NET NUnit和XUnit
JavaScript Jest是一个强大的测试工具

5. Stub

Stub是Mock的轻量级版本,它拥有Mock的结构,但无法记录交互或返回多个值。因此,当我们需要一个伪造的对象,但又缺乏Mock的灵活性时,应该使用Stub。

5.1 Stub对象

例如,假设我们构建了一个包含多个模块的应用程序。但是,其中一个模块尚未准备好。我们可能需要使用一个简化版本,临时提供预设的响应。让我们看看如何对AuthenticationService进行存根:

我们看到,每次尝试身份验证时都会返回一个默认用户。这将帮助我们完成应用程序流程,直到实际服务准备就绪。这可以用于测试,也可以在构建模块时作为临时替代方案。

注意,我们可以将Stub描述为Mock的一个特定用例。具体来说,当它系统地返回默认值时,我们无需验证交互。另请注意,这种方法类似于伪造Repository,尽管我们通常可以对接口进行stub。

5.2 Stub实践

许多库或框架通常认为Stub与Mock类似,因此从软件测试的角度来看,它有时可以与Mock互换。

6. Fake、Mock和Stub的区别

我们来总结一下Fake、Mock、Stub的区别:

Fake Mock Stub
功能有限的自定义对象 具有默认返回的类实例或方法 具有预设响应的类实例或方法
无法验证测试中的交互 可以验证测试中的交互 无法验证测试中的交互
测试期间缺乏灵活性 允许方法返回多个值 灵活性有限
预先计算的结果 与假货的精细互动 预先计算的结果
通常与库或框架无关 库和框架的坚实基础 通常是Mock库的一部分

7. 总结

在本教程中,我们描述了什么是Fake,以及为什么我们需要非真实的数据或对象,我们还了解了如何使用Mock或Stub来实现Fake。

我们了解了Mock为何比Stub更灵活,以及为什么它有更好的用途。此外,我们还注意到,在通用语言中,Fake、Mock或Stub的含义可以相同。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章