Nodejs学习笔记之测试驱动

分享第二章,关于测试驱动。这里的测试主要针对Web后端的测试 —— 你为什么要写测试用例(即测试用例的完善是否是浪费时间),如何完善你的测试用例,代码设计如何简化测试用例的书写,以及一些后期的构想。

1. 你为什么要写测试用例

这个习惯通常会被认为是一种耽误开发进度的行为,你需要花费几乎和开发代码相同的时间来逐步完善你的测试用例。但是在开发过程中,在开发完成一段代码后如果负责任而不是说完全把问题交给测试人员去发现的话,这个时候通常都会去做一些手动的测试。例如:

在代码中执行某些方法,查看输出的值是否符合预期。
修改数据库/缓存,然后执行某些方法,看数据库的变化是否符合预期。
使用工具模拟请求某些接口,查看接口的返回值/数据库的变化值是否会符合预期。
如果有前端页面的话,还会涉及到前后端联调,即要在前端页面上通过前端交互,查看前端的反馈是否符合预期,来间接验证后端代码的正确性。
现代化的测试工具都在尽可能的将这些人工的手动测试行为抽象成代码块,当你有意识去进行手动测试的时候,其实已经开始在尝试测试用例的行为了。既然可以通过手动的方式进行测试,那为什么还需要用代码来实现测试?

代码是可以复用或者在简单重构后可以实现更多的功能的,但是当你选择手动的时候,每次你都需要重头开始。
成熟的工作流中应当包括代码审核流程,代码审核的方式有很多,逐句阅读你的代码,或者检查你测试代码的完善性以及正确性,然后运行你的测试用例。后者更加简单。
当代码改动,例如修复 Bug 时候,很难保证你的改动是否会影响其他依赖你代码的部分。在人工测试的时代有一个叫做回归测试,即在你修复 Bug 将你的系统重新测试一遍。但是如果你已经有了完善的测试用例了呢,直接执行命令搞定。
当你重构代码的时候,同上。

2. 如何完善你的测试用例

在进入完善阶段前,先说说你将如何实现测试用例。

describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end end end

上面的代码来自于 Ruby 的 minitest。before 包含的代码块是在执行下面的测试用例前要做的事情,通常还会支持一个相对应的方法,在测试用例执行完执行。每个用例里面都进行一些很小的判断。

第一段中提到了一些手动测试里面经常会涉及到的测试内容,这里拿其中的 2 和 3 进行说明。在进行数据库相关的测试时,需要在 before 中插入一条测试数据,并且在 after 中删除测试数据。中间的测试用例中,通过执行相应的方法,执行完毕后:检查数据变化情况/检查是否有预期的异常/是否返回预期结果 来确认代码的正确性。如果是接口的话,就是通过代码发起对应的请求,然后检查返回的内容是否返回预期,有需要的话再去查看数据库里面的数据是否符合预期变化。

现在已经有了测试用例,但是任然需要考虑一种特殊情况。我现在为一个函数写了相对完善的测试用例了,跑完都 PASS 了,结果发现线上的日志里面还是有那个函数的报错。检查下发现函数的某个分支之前在测试的时候没有测试到,刚好线上的某种情况运行到了这个分支,结果有一个很不明显的语法错误报错了,有没有办法能确保所有的代码都测试过了?这里需要引入的是一个叫做 测试用例覆盖率 的概念,基本上每个语言都会有响应的实现。通过测试用例覆盖率,量化的告诉你你的测试用例有没有跑完某某文件里的所有代码,而你需要做的,就是尽可能保证你的覆盖率保持在 100%。

某种意义上来说,测试用例和测试覆盖率是用来提高开发者对自己代码自信心的工具。但是,他们也不是万能的。测试用例里面总可能会漏掉一些参数的可能性,当然你的代码里面也没有为这种可能性进行代码的编写,最终测试用例覆盖率只能告诉你你写的代码我们都帮你检测过了测试过了,对于你没有考虑到的可能性,表示无能为力。所以尽可能编写严格的代码,例如 javascript 里面尽可能都用 === 而不是 ==,使用强类型的编程规范等等,这些来降低这种因为接受的参数范围过大带来的潜在风险。

3. 代码设计如何简化测试用例的书写

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgjfyw.html