作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
达西安·弗洛里亚的头像

大夏的Florea

Dacian是一名资深的全栈移动应用开发者,也是Flutter框架的贡献者. 他专门从事严格的测试解决方案,帮助世界各地的公司设计和交付高质量的软件应用程序.

以前的角色

高级移动开发人员

工作经验

6

分享

你怎么样? 真的 感受 单元测试? 为了将团队的精力投入到改进代码库中,将焦点从开发中转移开来是否有意义? 开发人员并不总是喜欢计算代码的乏味循环, 模拟数据, 定义测试组及其功能签名, 编写测试, 然后回去调整现有的代码.

此外,管理人员必须权衡团队的资源和实现单元测试的成本. 选择退出单元测试的主要原因包括项目范围有限——无论是由于小团队, 或者在截止日期和预算问题上限制工作时间.

但是,添加单元测试就像在你把牛奶倒进咖啡之前检查它是否新鲜一样无缝和实用. 从长远来看, 单元测试无疑增强了项目的开发经验并降低了成本, 正如我们在探索单元测试的优点和其实现的流行策略时所看到的那样.

单元测试如何使您的项目受益

单元测试 在应用程序开发过程中,对小块代码(单元)进行集中测试以检查代码是否按照预期行为的过程. 通过单元测试预先识别故障可以减少调试时间并节省资金.

降低后期制作成本

通过深思熟虑 结合单元测试 在开发过程中,您可以提高应用程序的质量. 该应用程序的bug和故障更少 QA工程师 编写文档,或供开发人员修改.

较轻的后期制作工作量可以降低项目成本并更快地完成项目. 管理人员可以预测和预算缩短的QA团队合同期限, 安排即将开始的或相关的项目提前开始, 开发人员甚至可以更快地开始开发新的应用程序.

所有这些都将进一步降低成本. 知道你已经做好了避免潜在灾难的基础工作,这让人放心.

增加收入潜力

高质量、无bug的应用程序体验会带来更满意、更忠诚的终端用户. 如果用户通过留下积极的在线评论来推荐应用程序, 或者通过与朋友和家人分享, 这款应用的盈利潜力呈指数级增长.

我们不要忽视这样一个事实:负面评论少也能带来经济上的成功. 就像有消费者寻找有利的信息一样, 还有一些人专门寻找批评性评论,以此来避免出现有缺陷的产品或服务. (我认为自己属于后者.我们可以认为, 从商业角度来看, 获得正面评价是有益的,但避免负面评价是至关重要的.

临时文件

回顾单元测试可以帮助新开发人员加快应用程序的开发速度. 当一个项目被分割或分配给孤立的团队时, 对单元测试的回顾有助于填补知识空白,并提供对整个应用程序的深入了解.

不管团队的组织和沟通方式如何, 阅读单元测试使开发人员能够收集可能没有充分记录的信息, 或者被堆积如山的笔记所掩埋.

天生聪明的目标

就其本质而言,单元测试将项目划分为有序的、易于消化的部分. 这些个性化的代码划分有效地转化为明确定义的SMART(特定的), 可衡量的, 可实现的, 现实的, 和及时的目标.

会议 聪明的目标 通过使团队的进展对领导和涉众更加透明来服务于项目. 开发人员必须提前计划并以有组织的方式编写代码. 每个代码单元都有一个单独的功能, 并进行了测试,以确保该单元按预期运行. 该代码拥有 关注点分离:

  1. 每个项目单元支持一个单一的目标.
  2. 一个单元中的每个函数只完成它自己的作用域.

拥有小单元有助于项目的跟踪. 与里程碑不明确或遥远的团队形成对比, 经常检查一个又一个单元的团队可能是两者中更快乐的一个.

提高可伸缩性

计划良好的单元测试会在许多方面影响代码的可伸缩性, 使我们有能力:

架构师

  • 在合理的时间内添加或交换特性.
    • 有效实现关注点分离的直接结果

工程师

  • 增加工程师以更快地交付解决方案.
    • 更有目的性、组织性和可读性的代码的直接效果

团队

  • 将团队分成更小的部门,以更快地交付解决方案.
    • 模块化代码的直接结果

负载

  • 处理明显高于预期的使用率.
  • 更容易识别瓶颈.
    • 解耦代码的间接影响

可测试的代码 在其他环境中是否干净、模块化且可重用.

稳定的代码和特性

假设我们之前已经测试并实现了一个全局名称搜索功能, 现在想给最终用户过滤搜索结果的能力.

为了添加这个特性,我们将为我们的过滤函数创建一个新的单元. 我们可以确信,如果重新测试,控制全局按名称搜索功能的单元仍然应该通过. 新单元的代码不应该“破坏”其他单元的代码.

增强调试

识别和纠正 Bug的根本原因 在单元测试代码中是否更容易完成. 假设搜索功能不能正常工作. 您可以在项目的搜索模块中重新访问单元测试结果,而不是像大海捞针那样检查整个代码库以查找根本原因.

有效的重构

单元测试一个特性可以帮助我们确认它是否按预期工作——即使我们重构了代码逻辑, 或者更新第三方库, 例如.

继续我们前面的全局按名称搜索示例, 假设这个功能运行得很好, 但却慢如糖蜜. 为了解决速度问题,我们实现了一个潜在的修复(e.g.(替换算法)并重新测试. 再一次, 我们可以确信,我们没有“破坏”我们的功能,我们之前的测试是完美的. 该特性在重构后仍应通过单元测试.

单元测试策略

在测试方面,没有一个通用的标准. 事实上,有很多 专家讨论 为了使项目成功,需要进行多少单元测试. 在投入的时间和代码质量之间存在权衡. 在确定了单元测试的范围之后, 项目经理必须从多个单元测试策略中进行选择.

单元测试范围

将单元测试视为保护项目的保险策略. 经常, 一个人的风险承受能力决定了要购买多少保险,或者要实施多广泛的单元测试计划. 在这个范围的一端是那些愿意花更多的钱来获得更多的人, 他们的目标是为了避免或防止灾难而最大限度地覆盖. 另一端是那些愿意冒险的人. 也许他们在财务或其他方面具有弹性,能够从亏损中反弹, 如果发生的话. 绝大多数人介于这两种心态之间.

单元测试计划的范围通常分为三种模式:

  • 整个代码库的顺序
  • 按重要性排序的整个代码库
  • 只是关键的部分,也许是为我们的测试提供最大冲击的部分

第三种模式,称为 目标单元测试,在给定项目约束的情况下,通常是最实用的. 在这种情况下, 我们精心挑选要测试的代码, 关注对项目成功最关键的部分.

软件开发人员特别有资格将他们对每个代码片段用途的知识转化为合适的测试. 再来回顾一下咖啡的比喻:给定有限的测试资源, 我们大多数人都会同意,嗅一嗅可能变质的牛奶比嗅一嗅货架上可以优雅老化的糖要有价值得多.

一旦计划的范围确定, 我们需要考虑并采用一种适合我们项目的策略.

单元测试方法

行业标准为:

  • 实现后测试,即开发人员在功能实现后编写测试.
  • 测试驱动开发(TDD),其中开发人员为每个功能需求用例编写代码并进行测试.

实现后测试的想法可以吸引那些倾向于在向涉众提交可交付成果的竞赛中优先考虑开发的经理. 实现后的测试, 因此, 是比TDD更普遍的实践吗, 哪一个。, 相比, 开始缓慢,在整个项目期间需要纪律和耐心.

这两种方法采用相同的基本步骤,只是操作顺序不同. 下表显示了这些步骤, 当两种方法中相同的颜色匹配时:

实现后

TDD

步骤1. 将功能需求转换为用例.

步骤2. 实现代码.

步骤3. 定义测试用例.

步骤4. 编写、运行和验证测试.

步骤5. 必要时修改代码.

步骤6. 在所有测试成功后批准功能.

步骤1. 将功能需求转换为用例.

步骤2. 定义测试用例.

步骤3. 编写、运行和验证测试.

步骤4. 实现代码.

步骤5. 重新运行测试.

步骤6. 必要时修改代码.

步骤7. 在所有测试成功后批准功能.

如果不提到,这个讨论是不完整的 混合单元测试, 其中,我们在实现后测试特性,并使用TDD修复开发过程中遇到的错误, 为每个新bug添加测试.

技术的例子

我们已经看到了各种单元测试方法, 但这意味着什么准备我们的项目清洁, 实践中的差异化单元测试? 开始一个测试实现的例子, 我们必须首先提供关注点分离,其中每个单元支持单个目标,每个功能完成单个任务.

只有一个单元的接口应该被测试:内部状态和属性打算被其他单元读取和/或写入应该被排除. 因此, 如果一个单元负责一个乘法函数, 我们可以编写一个测试来确保乘法正确执行(e).g., 5x7=35),但我们不会研究乘法实际上是如何发生的(e.g., 5x7 vs. 7 x5和. 7 + 7 + 7 + 7 + 7,等等.).

假设我们有一个应用程序,我们希望在其中显示三个文本文件,它们的标题应该以蓝色字体显示. 我们首先为我们的特性编写整个程序, 加载文件, 显示我们的标题, 采用前面讨论的最佳实践和关注点分离. 然后,我们根据需要测试和更新代码.

现在我们的代码已经实现了,我们定义测试用例来检查整个特性:

  1. 加载文件吗??
  2. 文件文本是否显示?
  3. 标题的字体颜色是蓝色的吗?

接下来,我们为相应的代码单元编写单独的单元测试:

  1. 加载文件的函数
  2. 显示文本的函数
  3. 处理格式化的函数

我们可以在可读文件中检查这些场景 小黄瓜 语言:为表现这些行为而构造的语言:

功能:从文件中加载和显示文本,并以蓝色字体显示所有标题

    场景:用户加载文件成功
        给定用户导航到平台 
        用户导航到导入文件页面
        当用户选择文件并选择导入时
        文件导入成功 


    场景:文件加载成功,文本显示成功
        给定用户导航到平台 
        用户导航到导入文件页面
        当用户选择文件并选择导入时
        然后导入文件
        文件文本完整地显示

    场景:文件加载成功,文本显示成功,所有标题显示为蓝色字体
        给定用户导航到平台 
        用户导航到导入文件页面
        当用户选择文件并选择导入时
        然后文件文本完整显示 
        所有标题都以蓝色字体显示 

每个场景都需要进行单元测试.

实现后单元测试演示

在我们的实施后测试方法中,我们的步骤如下:

  1. 实现所有三个场景的代码.
  2. 为这些场景编写测试.
  3. 运行测试.

如果所有三个场景都通过了单元测试,那么我们的代码就可以运行了. 但是,如果任何测试失败,我们必须修改代码并重新测试,直到所有测试都成功.

我们可以预料到一些测试可能会失败, 因为它们不是与它们相应的特征同时发展起来的. 如果测试发现任何问题(例如.g.(如果标题不以蓝色字体显示),我们需要调整代码并重新测试.

TDD的演示

在TDD中,在编写任何代码之前,我们一个特性一个特性地将项目需求转换为测试. 由于我们还没有实现这个软件,我们的测试在第一次运行时失败了. 但我们还是这样做了, 以确认结构的完整性:代码的语法是否正确, 测试运行并失败. 但是如果它有缺陷,测试就不会运行,我们会得到一个语法错误.

现在我们实现代码并重新运行测试. 遇到的每一次失败, 我们更新代码并重新测试, 只有在测试成功后才批准该特性.

继续我们前面的例子,让我们来演示一下TDD. 我们定义并运行特性的测试(以蓝色字体显示的标题). 假设在我们的测试中没有检测到语法问题, 现在我们准备好实现我们的代码并重新运行测试:

  1. 为第一个场景编写测试.
  2. 实现第一个场景的代码,不断重复,直到所有测试都通过.
  3. 对其他场景重复步骤1和2.

一旦我们完成了这个过程,我们的TDD方法就完成了.

一盎司的预防

我们已经证明,对项目进行单元测试将节省更多的资金, 缺陷预防, 以及它给你带来的内心的平静.

本杰明·富兰克林的警句——“一盎司的预防胜过十分的治疗”——今天仍然适用. 就像保险单一样,单元测试是值得投资的. 我们进行测试是为了确信我们已经避免或阻止了潜在的灾难, 这是无价的.

Toptal工程博客的编辑团队向 里奥Trioni 用于回顾本文中介绍的技术内容.

了解基本知识

  • 单元测试的目的是什么?

    单元测试识别不按预期工作的代码. 这样做的时候, 它使您的团队能够在应用程序发布之前改进它, 提供高质量的用户体验, 防止虫子.

  • 单元测试的优点和缺点是什么?

    单元测试有很多好处:它执行最佳实践, 生成模块化和可重复的代码, 创建项目文档, 减少花费在项目上的总时间, 降低了项目成本. 单元测试的障碍包括有限的项目范围和/或预算.

  • 应该多久运行一次单元测试?

    经常. 单元测试应该在(a)将代码变更合并到代码库之前运行, (b)部署, (c)投入生产.

  • 应该对什么进行单元测试?

    一种观点认为所有的代码都应该进行单元测试. 但是许多团队限制了测试的范围,并且只针对代码库中最关键的区域, 或者那些为项目提供最大潜在回报的项目.

  • 单元测试在所有情况下都可行吗?

    No, 单元测试只有在应用了最佳实践来模块化被测试代码的情况下才有可能.

聘请Toptal这方面的专家.
现在雇佣
达西安·弗洛里亚的头像
大夏的Florea

位于 布加勒斯特,罗马尼亚

成员自 2020年11月9日

作者简介

Dacian是一名资深的全栈移动应用开发者,也是Flutter框架的贡献者. 他专门从事严格的测试解决方案,帮助世界各地的公司设计和交付高质量的软件应用程序.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前的角色

高级移动开发人员

工作经验

6

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.