所在的开发团队,由于测试人员(以下简称QA)的资源匮乏,较难保出品质量。所以会分一些任务给前端自己测试.单元测试又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

简单的说,单元测试是一种验证,验证代码功能,方法的实现的正确性。

为什么我们会区分前端和后端的单元测试?对于后端来说,单元测试并不陌生,验证一段逻辑的输入输出是否符合预期就可以,模式也很统一,毕竟编译型语言的本质就是计算。对前端而言,我们可能面对更多的是标记性语言和脚本语言,单元测试的边界很难定义,有渲染也有业务,如何测试也是很多项目争议的地方。

前端是不是要写单元测试?

首先,单元测试的重点在于单元,如何把代码拆分成一个个的单元,把业务和逻辑代码分开才是我们最开始需要考虑的问题。单纯对于一个结果的输入输出来说,很多时候浏览器给我们的信息更直观也更容易发现问题,这样看可能端到端的测试更适合我们,或者点一点,但是这样我们也很难发现代码内部的一些问题。

当然,比如你的处理很简单并且都和业务有关,逻辑计算通过一个请求都交给了后端处理;或者只做了一个展示界面,那么单元测试确实没有必要。

其次,为了以后可以快速定位bug和让别人接手起来更有信心的方面来看,单元测试在一些大型或者复杂的项目中确实有一定的必要。

前端单元测试到底测什么?

回到上一个问题,单元测试的重点在于单元,这也是前端单元测试的难点。现在我们大部分使用的框架大多把页面渲染和功能放到了一起,那些才是我们需要测试的单元?从相对的角度来说,一些不会经常变化的功能可以细分成单元进行测试:

1.公共函数

2.公共组件

越底层的代码越有测试的必要,因为UI的实现会依赖底层代码,例如我们可能用到的一些类似ramda、antd库,都会经过严格的单元测试,如果我们想要在项目中自己实现,就要对这样方法和组件进行测试,业务逻辑一般会跟着项目迭代和更新随时变化,写测试的意义不大。

单元测试的意义在哪里?

1.重构、重构、重构,重要的事情说三遍

TDD的具体实现就是通过红灯->绿灯->重构不断重复,一步一步去健壮我们的代码,所以单元测试的最大的意义也是为了我们今后可以重构我们的代码,只要保证测试的准确,就可以在重构中准确的定位到问题。同时也为以后的开发提供支持,在测试的基础上我们可以重构结构和业务功能。

2.单元测试是最好的注释

写注释是很多程序员都会忽略的一个步骤,或者改了代码你并不会记得去改注释,很多程序员会倾向于把变量名作为注释,但它无法很好的解释内部的逻辑,而测试会提示你那些步骤是可以通过、如何使用的最好文档,。更详细的规范了测试目标的边界值与非法值。

3.定位bug,减少bug

测试最直观的体现当然是与bug相关的,单元测试可以通过不同的条件来发现问题在哪里,在一些弱类型的语言中也避免了一些类型检查的低级错误,当然这个现在我们都用TypeScript做到了。

4.被迫的规范组织结构

可能平时我们会把一个方法写的很复杂、一个类写的很大,没有想过如何去组织结构,但如果你想到你即将的测试要如何写的时候,那可能你在开发前必须要想想哪些部分可以提出来了

2.前端单元测试怎么写

本文主要尝试解决三个问题:

  1. 在 TDD 做完Tasking列完实例化数据之后,完全没有UT基础不知道该怎么写单元测试?

  2. 在Vue应用的单元测试中,对UI组件和vuex store等测试的区别有何不同?颗粒度该细到什么程度?

  3. 测试收益如何最大化,如何配置高性价比的测试策略,即什么地方到底该花力气测试,什么地方又可以暂且放一放?

不谈论的包括:

  • ATT 验收测试 或 E2E 端到端测试,这个是我想进一步探索的话题,特别是在TDD的语境下。

  • 为什么要 TDD?但是我会讲为什么要 UT 单元测试。测试和TDD是两码事,而光是自动化测试的好处就已经足够多,但是如何做到更好的自动化和持续集成,那就需要TDD来指引方向。

  • Snapshot Testing 快照测试,其实我是很认可快照这种形式,但需要改进其工作流,至少结合Image Snapshot和Storybook等工具,甚至更应该放到CI上去。

下面我就来结合具体场景,进一步实例化这些问题,举几个:chestnut::

  1. 在 TDD 做完Tasking列完实例化数据之后,完全没有UT基础不知道该怎么写单元测试?

// Given一个完全没有UT基础的新人:walking:// When当他:walking:阅读和练习本文的Jest的部分// Then他能够把Given/When/Then的套路学会他能够学会Jest的基本用法,包括测试suite和断言等语法他能够学会Jest中测试异步的几种方式
  1. 在Vue应用的单元测试中,对UI组件和vuex store等测试的区别有何不同?颗粒度该细到什么程度?

// Given一个有基本的UT知识但没写过Vue测试的新人:walking:// When当他:walking:阅读和练习本文的Vue单元测试的部分// Then当然,他能够学会Vue组件在测试当中的几种渲染方式他能够学会UI组件的分类,特别是交互行为的测试方式他能够对Vuex概念的理解更加深入,且知道 `Redux-like` 架构的好处他能够合理测试vuex store的mutation和getter中的业务逻辑他能够测试组件如何正确dispatch action以及action中如何做异步操作
  1. Vue项目中测试收益如何最大化,如何配置高性价比的测试策略,即什么地方到底该花力气测试,什么地方又可以暂且放一放?

// Given一个具备UT基础但找不到着力点的求索之徒:monkey:// When当他:walking:阅读本文的Vue应用测试策略部分// Then他能够找到测试的重点,重新燃起对UT的热情:fire:他能够在项目背景下合理配置单元测试的测试策略

于是乎,这就是本系列文章的大纲,先放出来给大家一个对于Vue应用单元测试的全局观:

## 单元测试基础### 为什么选择 Jest### Jest 的基本用法### 该如何测试异步代码?### 单元测试与自动化的意义## Vue 单元测试### Vue 组件的渲染方式### Wrapper `find()` 方法与选择器### UI 组件交互行为的测试## Vuex 单元测试### CQRS 与 `Redux-like` 架构### 如何对 Vuex 进行单元测试### Vue组件和Vuex store的交互## Vue应用测试策略### 单元测试的特点及其位置### 单元测试的关注点### 应用测试的测试策略

:hushed: 哦豁,正文终于开始……

单元测试基础

个人 鲜明的观点就是: 写不好是能力问题,不写则是态度问题 。单元测试客观上可以让开发者的工作更高效,Vue 应用的单元测试是一定要的。

单元测试的上下文

谈任何东西都一定要有个上下文。你的论述不能是「因为单元测试有这些好处,所以我们要做单元测试」,而应该是「不做单元测试我们会遇到什么问题」,这样才能回答「为什么要写单元测试」的问题。那么我们谈论单元测试的上下文是什么呢?不做单元测试我们会遇到什么问题呢?

Vue 应用单元测试的策略与实践 01 - 前言

上图为一个产品从 idea 分析、设计、开发、测试到交付并获取市场反馈的过程。

而 单元测试的上下文就是存在于「敏捷」当中 。敏捷为的是更快地交付有价值的可工作的软件。为此,它有一个指标来度量这个「更快」,那就是 lead time,它度量的是一个 idea 从提出被验证,到最终上生产环境面对用户的时间。显然,这个时间越短,软件获得反馈的时间就越短,对价值的验证就越快发生。

单元测试的意义

这个结论对我们写不写单元测试有什么影响呢?答案是,不写单元测试,你就快不起来。为啥呢?因为每次发布,你都要投入人力来进行手工测试;因为没有测试,你倾向于不敢随意重构,这又导致代码逐渐腐化,复杂度使得你的开发速度降低。

那么在这个上下文中来谈要不要单元测试,我们就可以很有根据了,而不是“开发爽了就用,不爽就不用”这样含糊的答案:

if-else

除此之外,你就需要写单元测试。如果你想随时整理重构代码,那么你需要写单元测试;如果你想有自动化的测试套件来帮你快速验证提交的完整性,那么你需要写单元测试。

单元测试与自动化的关系

综上,我们用来谈论单元测试的「透镜」是什么呢?一言以蔽之,两点: 反馈速度 和 自动化 。

自动化回答的是 要不要自动化的单元测试 这个问题。测试是重构的唯一保障,也就是说,没有测试,基本上就没法重构代码(重构指的是 不改变软件可观测行为的前提下改善代码内部设计 ),基本上就只能看着代码腐化。那么,基本上只要你的系统需要持续发展,你就需要单元测试。

反馈速度回答的是 要不要 TDD、测试先行还是后补 这个问题。答案是,需要 TDD,最好先行,因为可以提高反馈速度 ,缩短反馈周期,与此同时减少不必要的浪费。至此,回答了「为什么我们需要写单元测试」的问题。下面让我们来谈谈如何写好 Javascript 代码和 Vue 应用框架的单元测试。

为什么选择 Jest

Jest是一个“零配置”的前端测试工具,具有诸如模拟和代码覆盖之类的开箱即用特性,主要用于React和其他JavaScript框架。

我们团队对采用JEST做前端测试的结果非常满意。它提供了一种“零配置”的开发体验,并具备诸多开箱即用的功能,比如 Mock 和代码覆盖率等。你不仅可以将此测试框架应用于React.js应用程序,也可以应用于其他 JavaScript 框架。Jest 经常被炒作的功能之一是用户界面的快照测试。快照测试可以作为测试金字塔上层一个很好的补充,但请记住,单元测试仍然是坚实的基础。

Jest 的几大好处可以涵盖为:

  • Fast 天下武功,唯快不破。确实很快,虽然实测下来跟 Mocha 4 还是慢了些,以后找个机会再测一次。

  • Opinionated 不需要你做出选择和配置,就能提供所有的东西,比如Mock(干掉Sinon)、Test Runner(干掉Karma)、Matcher(干掉Chai)、Test Coverage(内置istanbul)

  • Watch Mode 注重开发者体验,能够在编码的时候帮助我们快速获得测试结果的反馈。

  • Snapshot Testing 这一点是值得争议的一点,前文也提到过会专门开个issue来讨论,在此不再赘述。

总结一下

Jest 最大的特点是它是一个非常有效的解决方案,不需要与其他测试库交互来执行它的工作。与此同时 Jest 非常注重 开发者体验 ,这一点也是特别值得欣赏,现在市面上关注开发者(“人”)体验的开发框架和 工具实在不多,而Jest Watch模式的核心就在于快速获得反馈,虽然我没在命令行使用而是WebStorm但亦可以与之结合。

ps: 除此之外,还有很多开发者体验亦值得细细品味与发现,特别是Jest本身来自Facebook的工程化支持也是特别棒的。

后面的内容我们主要以jest+Vue Test Utils (是 Vue.js 官方的单元测试实用工具库)为例来讲解单元测试。