测试驱动javascript开发 -- 4.测试驱动开发过程(下)

简介:   TDD是一个迭代的开发过程,他包括下面的步骤:1.编写测试;2.运行测试,观察失败;3.确保测试通过;4.重构,减少重复。   每次迭代中,测试就是规范。在我们完成开发之后,测试就可以通过了。之后我们就需要进行减少重复代码和提高设计的重构工作,然后再次运行测试,并保证其通过。

  TDD是一个迭代的开发过程,他包括下面的步骤:1.编写测试;2.运行测试,观察失败;3.确保测试通过;4.重构,减少重复。

  每次迭代中,测试就是规范。在我们完成开发之后,测试就可以通过了。之后我们就需要进行减少重复代码和提高设计的重构工作,然后再次运行测试,并保证其通过。

  虽然TDD不主张预期的大设计,但是我们在TDD开始之前还是需要做个简单的设计。我们要如何写自己的第一个设计呢?当我们获得了足够信息可以制定测试的时候,测试代码的编写,本身就是设计。我们指定在特定情况下特定代码的行为,系统之间组件如何响应,以及他们之间如何组合。下面我们会举例,以便大家更好的学习。

  TDD中的迭代时间很短,我们需要非常清楚我们所处的阶段。无论何时我们对代码进行了修改,或者移出了某些功能,我们需要把他们记录在todo列表中,加以观察。这个列表可以是一张纸,或者记事本之类的东西,只要方便你快速的查找和记录既可。在处理这些新的修改点之前,我们需要首先结束本次迭代。本次迭代结束之后,我们再从todo列表中取出一个任务,开始下一次迭代工作。

 

  步骤1:编写测试

  每次TDD迭代,首先要做的事情是选择一个你要做的功能,为它编写单元测试。单元测试需要简短,测试聚焦在function上的某一个功能点上。比较好的编写测试的规则是,编写尽量少的测试代码就能让测试失败。当然,测试断言不能和之前的测试重复。如果一个测试涉及到系统的两个或两个以上的方面,就说明要么是这个测试太大了,要么是里面包含了重复的测试点。

  测试需要能够描述我们实现的功能,我们的功能代码没做修改,就不需要修改测试代码。

  假设我们要完成一个String.prototype.trim函数的开发,用以去除字符串前后空格。对该方法好的测试,第一步应该是测试前空格是否删除了。

testCase("String trim test", {
  "test trim should remove leading white-space":
  function () {
    assert("should remove leading white-space", "a string" === " a string".trim());
  }
});

  严谨起见,我们需要先判断字符串包含trim方法。这是因为我们添加的全局函数可能会和第三方代码发生冲突,在代码之前添加 typeof "".trim == "function",可以帮助我们在运行测试之前发现问题。

  为单元测试提供输入条件,运行之后他会判断输出条件是否和预期一致。输入条件不仅仅是函数的参数,任何函数的依赖项,例如全局作用域、某些对象的特殊状态,这些都是输入条件。与此类似,输出结果包括返回值、全局作用域或者相关对象。我们通常把输入输出项分为直接和间接两种。直接项:函数的参数和返回值;间接项:不是以参数形式传入的参数和被修改的外部对象。

 

  步骤2:观察失败的测试

  测试准备好之后就可以运行了。有很多原因促使我们在编写实现代码之前运行测试,最主要的一点是我们可以用它来确定代码的状态。在写测试的时候,我们会有一个比较明确的期望,测试如何会失败。单元测试虽然不存在逻辑的分支,代码也比较简单,但他也像其他代码一样存在bug。但是运行它,比较期望结果,我们会很快发现这些bug。

  理想情况下,当我们添加了新的测试的时候,我们应该可以运行所有测试用例。这样我们就可以很容易的抓取到干扰测试,例如一个测试依赖于另外一个测试。

  编写实现代码之前运行测试,可以告诉我们一些新的事情。有时候会有这样的经历,在我们写任何实现代码之前测试可以正常通过。一般情况下,这样的事情不应该发生,TDD教导我们编写不能通过的测试。因为我们是先写测试代码,功能代码还没有开发,这时测试代码能跑通就说明存在问题。我们需要确定问题的来源,是不是运行环境已经提供了该方法的实现,或者我们有没有必要保留这条测试用例。

 

  步骤3:确保测试通过

  准备好运行失败的测试代码之后,我们要做的就是编写实现代码,并保证测试代码可以运行通过。有时候我们甚至需要硬编码,不必担心在这个步骤中我们的代码是多么糟糕,在后面的重构环节我们可以优化他。编写实现代码的时候,我们要寻找最明显的简洁实现方式,如果没有我们可以伪造他,而把具体的实现拖延到后面。

  1.你不需要他

  在极限编程中,TDD的精髓是“你不需要他”,意思是直到需要的时候才需要添加相关功能。基于假设添加一些以后可能会用到的代码,会让我们的代码变得很膨胀。对于动态语言,特别是javascript,违反这一原则有时候对我们是有诱惑的,他可以增加代码的灵活性。一个例子是,为函数添加过多的参数。除非有那样的需求,否则不要这样做。

  2.通过String.prototype.trim测试

  下面的代码是为满足之前的测试开发的。

String.prototype.trim = function () {
  return this.replace(/^\s+/, "");
};

  可以看到,这个实现还不完善,只去除了左空格。但是TDD就是这样的,每一步都很小,只要能让测试通过就可。发现新的需求点后,编写测试代码,然后完善实现代码并通过测试。

  3.能够工作的最简单的方案

  最简单的解决方案有时候意味着,可能要往产品中添加硬编码的代码。因为有时候一般的解决方案可能不是那么明显,我们可以用硬编码的方式推进我们的项目,等到有了解决方案的时候再替换。虽然硬编码可以推进我们的项目,代码质量是我们的最终目标。

  步骤4:移除重复的重构

  最后,最重要也是最有趣的工作就是使代码变得整洁。当实现代码开发完毕,测试顺利跑通之后,我们就可以考虑重构的工作了,把一些重复的代码移除。这期间只有一条准则:测试必须能跑通。关于重构好的建议是,每次只对一个操作进行修改,并保证测试能够通过。重构是对已有代码的维护修改,所以测试不能失败。

  重复的代码可能会出现在不同位置,有时候他是为了解决硬编码的解决方案。如果我们有一个硬编码的假冒响应,我们需要给他添加另外的测试,让他在不同输入的条件下失败。或许我们一时还想不到替换硬编码的方案,但至少我们知道问题的存在,他为我们提供了足够的信息,方便我们找到最终解决方案。

  重复的代码同样可能存在于测试代码中,例如setup中的请求对象和假冒的依赖。测试代码也是代码,同样需要维护,移除重复内容。如果测试代码和系统过于耦合,我们需要抽取帮助方法和对结构进行重构。setup和teardown可以用来集中设置对象的创建和销毁。

  我们在重构的过程中不能让测试失败。如果在重构的过程中我们没有用更少的代码完成工作,我们就需要考虑把工作托到以后再做。

  步骤5:重复工作

  一旦所有工作都完成了,没有重复代码了,也没有重构工作需要做了,这时候就从todo列表中找一个新任务,重复上面的步骤。根据需要重复这样的工作。你熟悉了这一过程之后,可以放大脚步,但是确保你的周期很短,这样可以得到及时的反馈。

  功能满足需求之后,我们可以考虑提高测试的覆盖率,可以添加对边界值的测试、对依赖项的测试、不同输入类型的测试、不同组件之间的整合测试等。下面是我们为String.prototype.trim添加的第二条测试:

"test trim should remove trailing white-space":
function () {
  assert("should remove trailing white-space", "a string" === "a string ".trim());
}
目录
相关文章
|
4月前
|
人工智能 自然语言处理 测试技术
从人工到AI驱动:天猫测试全流程自动化变革实践
天猫技术质量团队探索AI在测试全流程的落地应用,覆盖需求解析、用例生成、数据构造、执行验证等核心环节。通过AI+自然语言驱动,实现测试自动化、可溯化与可管理化,在用例生成、数据构造和执行校验中显著提效,推动测试体系从人工迈向AI全流程自动化,提升效率40%以上,用例覆盖超70%,并构建行业级知识资产沉淀平台。
从人工到AI驱动:天猫测试全流程自动化变革实践
|
7月前
|
数据采集 人工智能 监控
人工智能驱动的软件工程:测试左移的崛起价值
本文探讨了人工智能驱动下测试左移理念在软件工程中的重要性,分析测试工程师在需求评估、AI代码生成及遗留系统优化中的关键作用,揭示AI带来的挑战与机遇,并指出测试工程师需提升技能、关注合规与可维护性,以在AI时代保障软件质量。
426 89
|
11月前
|
数据采集 算法 测试技术
【硬件测试】基于FPGA的1024QAM基带通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文介绍了基于FPGA的1024QAM基带通信系统的硬件测试版本,包含testbench、高斯信道模块和误码率统计模块。系统新增ila在线数据采集和vio在线SNR设置模块,支持不同SNR条件下的性能测试。1024QAM调制将10比特映射到复平面上的1024个星座点之一,实现高效数据传输。硬件测试结果表明,在SNR=32dB和40dB时,系统表现出良好的性能。Verilog核心程序展示了各模块的连接与功能实现。
316 7
|
5月前
|
存储 测试技术 API
数据驱动开发软件测试脚本
今天刚提交了我的新作《带着ChatGPT玩转软件开发》给出版社,在写作期间跟着ChatGPT学到许多新知识。下面分享数据驱动开发软件测试脚本。
178 0
|
10月前
|
机器学习/深度学习 人工智能 并行计算
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
|
8月前
|
传感器 人工智能 JavaScript
鸿蒙开发:DevEcoTesting中的稳定性测试
DevEcoTesting主要的目的也是用于软件的测试,可以让开发者无需复杂的配置,即可一键执行测试任务,同时提供了测试报告和分析,无论是对于开发者还是测试同学来说,都是一个非常方便的工具。
293 3
鸿蒙开发:DevEcoTesting中的稳定性测试
|
11月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
598 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
7月前
|
敏捷开发 运维 数据可视化
DevOps看板工具中的协作功能:如何打破开发、测试与运维之间的沟通壁垒
在DevOps实践中,看板工具通过可视化任务管理和自动化流程,提升开发与运维团队的协作效率。它支持敏捷开发、持续交付,助力团队高效应对需求变化,实现跨职能协作与流程优化。