likes
comments
collection
share

再谈单元测试

作者站长头像
站长
· 阅读数 16

曾写过多篇测试相关的文章,都源于每次测试给我带来的触动,这次也不例外,最近在重构复杂逻辑时,再次感谢它为我保驾护航,同时也发现了历史测试中存在的一些问题。

测试的意义

这是一个认知问题,如果不清楚为何要写测试,那么最终产生的测试大概率是腐烂、或无意义的。

  1. 保证代码的正确性,提升代码质量是它最高的使命。
  2. 保证代码的安全性/稳定性,当对某段代码改动后,测试会告诉哪些代码受到了潜在的问题。
  3. 可以指导写代码的思路,在测试驱动开发(TDD)尤为明显。
  4. 更能透彻的理解代码,很多情况下,写代码时常常会陷入自己的世界,从测试相当于站在旁观者的角度审视代码,从而提升可读性和扩展性。

写测试的一些原则

首先必须要有一个态度/认知:写测试与写普通代码一样,测试也要很清楚的表达逻辑,不能因为它不体现到产品上就粗糙了事。

有些原则会受不同开发者个人能力影响,所以我的看法是正面原则应该鼓励,反面原则明令禁止。

不写没有意义的测试

比如 1 + 2 === 3 ,或者让函数执行一次,而不是验证函数执行的结果,这种无意义的行为,还有一种情况是为了提升测试覆盖率而补充,在我看来更是“本末倒置”。

不写重复的测试

相同的逻辑写多遍,当然有可能是不同开发成员测相同的逻辑,如果发现应该删除。

不写描述与实际逻辑不符的测试

典型的挂羊头卖狗肉,当修复测试时,通过描述不能准备判断场景,细排查发现并不是预期逻辑,花费了不必要的时间。

不写上下文依赖的测试

要保证测试的独立性,就是说单个的测试也可以运行,在工作当中也很多处发现,下一个测试要依赖于上一个测试,这显然是不合理的,如果某天删掉了第一个测试,第二个测试就会挂掉。

尽可能保证测试的可读性

最好有递进关系,很容易从测试中看到你的逻辑是什么。

尽可能保证测试的纯粹性

一个测试只测一种场景,也可以说编程原则中的“单一原则”,可以正反面断言。

提取重复的前置条件

很多时候只为了测关键点,需要配备一些前置条件,当多个测试都有相同逻辑时,应当提取到不同维度。

比如使用 before、beforeEach、after、afterEach,或者提升至更底层的函数,比如 Helper 类。

反面示例(下图)很多是本人曾经犯过的问题,自觉公开处刑 🙃

再谈单元测试

应该如何写一个测试?

关于测试的方法论,本人所知道的是 TDD 和 BDD,也曾查过一些资料,对我至今仍然有非常大的指导意义,不讲过多的概念,回顾一张图吧。

再谈单元测试

几个例子

  1. 开发一个创建用户的 API,我的步骤是:

    a. 编写测试用例 :先 http 请求这个 API 的路由,此时路由可能都不存在,没有关系,这一步就是要 这个测试用例失败

    再谈单元测试

    b. 编写代码 :解决它为什么失败?原因:没有定义路由和响应数据,OK 解决它,添加一个 POST /api/user 的路由,并且 response.end('success')这一步让测试用例通过

    再谈单元测试

    c. 编写测试用例 :制定创建用户的信息:名字(张三)、手机号(xxx)、年龄......,并且期望创建成功,并且判断创建的信息无误,此时运行测试, 结果应当是失败 ,因为代码中还没有创建逻辑。

    再谈单元测试

    d. 编写代码: 解决失败原因,创建响应结果的实体,并且 response.send({ code: 200, data: user }), 让测试成功。

    再谈单元测试

    e. 编写测试用例 :输入不合法的信息,让 测试继续报错

    f. 编写程序: 加入校验逻辑,解决报错,让 测试成功。

    g. ......

  2. 一个缺陷,真实开发的例子:

a. 这个思路应当是先按照描述去模拟场景,验证预期行为和实际行为是否一致,如下:

再谈单元测试

b. 这种情况就避免了 启动前端代码的麻烦事,进行自我验证,驱动排查 。在解决这个缺陷时,重构了大量的代码,历史的测试一次次告诉我重构逻辑漏掉了哪些场景。

从上面的两个例子中,可以再次得出它的指导思路: 让测试失败,改代码修复,在让测试失败,修复......反复验证。

结束

好吧,暂时没有其他要说的了,后续有想法,再继续更新,如果觉得文章写的不错,点个赞吧 🌹

转载自:https://juejin.cn/post/7241902548616118333
评论
请登录