如今开始为你的Angular应用编写测试

image

DevUI是一支兼具设计视角和工程视角的团队,服务于华为云 DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师。

官方网站: devui.design

Ng组件库: ng-devui(欢迎Star)

官方交流:添加DevUI小助手(devui-official)

DevUIHelper插件:DevUIHelper-LSP(欢迎Star)

引言

业务需求变化快、涉及大量的UI 和交互、大部分业务场景是在与后端交换并处理数据。以上几点事实让测试成为前端领域让人很是头疼的问题,也成为前端不写单元测试的借口。

html

当咱们面临一个不太熟悉的领域或场景时,简单可快速实施的内容更容易让咱们迈出第一步。本文的目的正在于此,让你二十分钟以内能够完成单元测试的spike 内容,以及将相关内容移植到项目代码中。前端

本文包含三部份内容:git

(1)为何要写测试,包含了不少常见的关于测试的观点、误解和策略程序员

(2)如何书写测试?经过spike 来看一个典型测试的构成github

(3)20 分钟把单元测试集成到已有项目中chrome

01 为何要写测试?

写测试是好的

前端业务因为自身的特殊属性,单元测试很差写。这么长时间以来没写测试,感受也没什么问题。另外,每一个迭代的业需求量那么大,光搞业务开发都已经焦头烂额了。之前我一直秉持这样的观念,因此就历来没有写过单元测试。

segmentfault

注:文章后续提到的测试若是不注明前缀,都是指单元测试。单元测试的执行速度快,覆盖范围广。按照测试金字塔原理,单元测试的覆盖率应该达到100%!

后端

看过《重构》、《JavaScript测试驱动开发》和《Google软件测试之道》这三本书后,我找到了不起不开始写测试的理由:数组

(1)测试是活的文档。代码是给人读的,相比于注释,测试更容易让人读懂;
(2)测试有利于重构。有单元测试的代码在功能扩展和重构时,操做成本更低;
(3)测试能提高代码质量。一旦开始写测试,你就会发现本身的代码组织结构多么混乱,会逼着本身从代码的源头设计来解决问题,写出可测试的代码,下降代码圈复杂度。

前端工程师

关于代码的圈复杂度,团队里的同事曾经写过一篇文章来专门介绍,感兴趣的能够参考:https://juejin.im/post/684490...

另外关于圈复杂度分享一个很是amazing的小知识。Bob大叔说:理想的方法长度不该该超过四行代码。没错,就是写出《程序员的职业素养》一书的Bob大叔,这是一本你不论在什么时间段看必定会有所收获的书。若是你尚未读过,墙裂推荐。

鉴于此,我下定决心要给本身的项目写单元测试,而且已经隐约有了眉目,应该如何去写。

人人都难以开始动手的缘由

上面扯了那么一大堆,其实你们都明白。“道理我都懂,就是以为很难如下手”。因此在开始以前,咱们必需要先回答两个问题,也是大多数人迟迟不肯意开始书写测试的缘由:

写测试会该开发者带来极大的成本

要达到或者接近100% 的单元测试覆盖率,代码量基本上要翻倍。好比说,知名的验收测试工具FitNesse 拥有6.4 万行代码,其中2.8 万行代码是单元测试代码(数据来自《程序员的职业素养》)。这是一个让人望而生畏的比例,我也曾一度对“写测试会给开发者带来极大的成本”这个观点深觉得然,直到我看到这样一句话,在此拿出来跟你们分享:

有人会告诉你TDD 能减小缺陷,可是有成本,你会编写比产品代码多两倍的测试代码,因此会下降速度。这种假设认为影响软件开发的因素是打字速度,但这不是真的,实际上的影响因素是阅读需求文档、编写文档、开会、定位和修复bug。

这句话就不进行解读了,你们细品。

前端在测试领域遇到的种种困难

也就是咱们在引子里提到的那些问题,既然从上述角度来看,很是难以入手。那咱们不妨以另外两个基本事实为切入点,看看可否有所改善。

(1)每一个项目中都会有一些工具用来处理若干组件公用的业务逻辑,好比说特定时间日期的转换等,这些方法一般分布在普通的ts 文件或者service.ts 里面
(2)Angular 项目中会用到大量的pipe,pipe 实际上就是一个处理输入输出且逻辑稳定的函数
看到这两点,相信你的脑海中可能已经浮现出具体的函数名,咱们就从这部分代码开始。

02 如何开始?

建立一个spike

所谓的spike(中文可翻译为【轻轻地刺】),是指当咱们面临一个不太熟悉的技术领域的时候,先抛开目前已有的东西,迅速从零开始创建一个demo,以考察咱们的方向是否可行。每一个人在平常工做中应该都有过运用spike 解决问题的经历,尤为是在进行带有技术预演性质的工做时。只是可能不少人不知道该如何来描述这个过程。

回到正文,大多数团队面临的状况多是,个人代码目前运行良好(至少not bad),每一个迭代有大量的需求要作。团队里面一直没有写测试的传统,也没有人关心。要在这样一个快速演进的项目中集成本身不太熟悉的技术领域,确实是一个麻烦事。所以,咱们先来作一个spike。用ng-cli 新建一个项目完成咱们对Angular 单元测试的探索。在spike 当中,咱们能够抛开全部规范、设计层面的约束,只是为了探索和验证。

首先,咱们用ng-cli 建立一个新的项目,这个操做实在是再简单不过了

ng new spike-test

而后

cd spike-test
ng test

就像上面那样,咱们已经完成了一个最简单的包含单元测试的项目,这个过程大概只花了你3分钟的时间?如今咱们把这个项目拆开看看,以便把必要的信息提取出来集成到咱们已有的项目中。

经过spike查看测试的组成

主要来看看app.component.spec.ts 这个文件

从这个测试文件中,咱们能够看到,要完成一个测试,首先要引入一些跟测试相关的包和被测试的组件(或是服务、管道甚至普通的ts 文件)。

一个测试套件(describe)里面会包含若干个should,也就是测试应该验证的行为,在这个例子中就是如下三点:app 应该被建立、title 应该是spike-test、title 应该配渲染在h1 tag 里。

除了这个,还须要用到不少相关的配置文件。可是总的来说,仍是很是简单。既然如此,那么就开始动手吧,在已有的项目中集成测试用例。

03 如何在已有项目中集成测试?

安装测试依赖并添加相关配置文件

在这个环节中遇到的全部问题,好比说文件位置放在哪里,文件的内容是什么等,均可以参考咱们上一小节已经作好的spike 项目来进行配置。

(1)确保根目录下有karma.conf.js 配置文件
(2)确保src下有test.ts 文件
(3)确保你的spec.ts 文件没有被tsconfig 忽略,主要检查tsconfig 文件中的include 和exclude 配置
(4)确保有安装如下几个依赖,若是没有,先所有安装上

"karma": "^5.2.1",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "^4.0.1",
"karma-jasmine-html-reporter": "^1.5.4",

来看一个例子

在开始写以前,咱们先梳理下写测试用例的基本步骤:

(1)先经过手写列表记录下你想要测试的行为,也就是上面spike 中的若干个should
(2)按照上述列表书写测试用例

以上依赖和配置弄好之后,咱们就能够开始尝试在已有的项目中编写测试用例,假设咱们有一个将字节(Byte)转换为经常使用大小表示的方法(好比KB、MB、GB),方法的代码以下:

如上所述,咱们先开始准备测试行为列表:

  • 当输入小于0或者非数字的时候,须要返回--
  • 当输入是数字且小于100的时候,单位为B
  • 当输入是数组且大于等于100小于100000的时候,单位为KB
  • ......MB
  • ......GB

而后开始为这个公共方法书写测试代码,首先新建一个测试文件test.spec.ts,在文件中引入这个方法

import { transferSize } from './common-method';

接着添加你的测试套件和若干个should

describe('TransferSize Method Of Util', () => {
    it(`should return '--' when the input is less than zero`, () => {
        expect(transferSize(-1)).toEqual({
            sizevalue: '--',
            sizeunit: ''
        });
    });
    it(`should return '--' when the input is not number`, () => {
        expect(transferSize('haha')).toEqual({
            sizevalue: '--',
            sizeunit: ''
        });
    });
    it(`should return 'xB' when the input is less than 100`, () => {
        expect(transferSize(0)).toEqual({
            sizevalue: '0',
            sizeunit: 'B'
        })
    });
    it(`should return 'xMB' when the input is more than 100`, () => {
        expect(transferSize(100)).toEqual({
            sizevalue: '0.1',
            sizeunit: 'KB'
        })
    });
    it(`should return 'xGB' when the input is more than 100000`, () => {
        expect(transferSize(100000)).toEqual({
            sizevalue: '0.1',
            sizeunit: 'MB'
        })
    });
    it(`should return 'xGB' when the input is more than 100000000`, () => {
        expect(transferSize(100000000)).toEqual({
            sizevalue: '0.1',
            sizeunit: 'GB'
        })
    });
});

写完了,运行ng test,成了!

一个最简单的单元测试用例已经被咱们集成进了已有的项目中。

若是你还想看项目中每一个文件的代码测试覆盖率,能够运行这个命令:ng test --code-coverage,固然记得在你的Karma.conf.js 中配置coverage 文件夹的路径

这样配置完成之后,每次你执行完上述命令,一个更详细的结果会产生了项目根目录下的coverage 文件夹中,打开文件夹中的index.html 文件能够看到每一个文件的测试覆盖率及未覆盖代码。

04 总结

为了达到快速实施的目的,本文只是将必要的部分串联起来组成了一个可操做的最小化demo,还有许多关于测试的问题没有解答,好比各类测试覆盖度的计算方式,如何利用mock & stub 进行接口交互业务测试,如何针对像DevUI 这种组件库场景下进行测试。

关于上面提到的全部未解答问题,我们下期见。

加入咱们

咱们是DevUI团队,欢迎来这里和咱们一块儿打造优雅高效的人机设计/研发体系。招聘邮箱:muyang2@huawei.com。

文/DevUI 少东

往期文章推荐

《前端有了这两样神器,不再用追着后台要接口啦》

《Web界面深色模式和主题化开发》

《手把手教你搭建一个灰度发布环境》