可以进行单元测试么_前端与单元测试

d5e76515d19c93d1aeea44be6be88551.png

先来几个专业词汇,这样显得高大上一点(不存在的=。=)

BDD: Behavior-Driven Development (行为驱动开发)TDD: Test-Driven Development (测试驱动开发)ATDD: Acceptance Test Driven Development(验收测试驱动开发)

好,说完了,然后我们废话不多说,直接进入正题。我会从多个测试框架入手,结合各种断言库,用代码方式说明。


单元测试(Unit Testing),是指对软件中的最小可测试单元进行检查和验证。

当今所有著名的框架都要进行单元测试,经过测试的框架,它的信任度显然高于未测试的框架。

这里,我们介绍一下karma这个前端的单元测试框架。

4c567b916e296056d25d4ea8e73d387b.png
Spectacular Test Runner for Javascript​karma-runner.github.io

首先我们来安装一波:
新建一个文件夹,然后在空文件夹中打开终端输入

npm init -y
(sudo) npm install karma-cli -g
npm install karma karma-jasmine karma-chrome-launcher jasmine-core --save-dev
npm install karma-phantomjs-launcher --save-dev

b809e3745b98b47697e4e670227fe9fe.png

你安装karma-cli这个倒是说得过去,可是这个jasmine是啥,这个chrome-launcherphantomjs-launcher又是啥?

没错,单说测试框架是不完整的,必须要有断言库与之相配合,这里的jasmine就是断言库。

48a8a443374daaab5d736b0a815e787c.png

啥是断言(assert)

根据概念:

断言是编程术语,表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。

一言以蔽之,老子/老娘说啥就是啥!听起来好像挺霸道的。那么具体呢?

顺着karma的正常流程向下走,我们来写一个简单的单元测试。在终端输入:

karma init

你会发现,需要做一个调查问卷了,问题如下:

> 请问你要用哪种测试框架呢?
> 按tab键选择,按回车键进入下一个问题。
> jasmine 
(因为我们安装的是jasmine,选什么断言库都别忘了安装一下)> 您想要使用Require.js么?  
> 选择yes的话,会安装Require.js插件。
> 按tab键选择,按回车键进入下一个问题。  
> no 
(这里我们选择no)> 你想要在什么浏览器中测试呢?  
> 按tab键选择,输入空字符串进入下一个问题。  
> Chrome
> PhantomJS
>注:上面的选择这两个浏览器的原因是我们之前安装了这两个浏览器的启动器(launcher)> 需要测试的源文件和测试命令文件放在哪呢?
你可以使用通配符(glob patterns)来匹配文件,比如:"js/*.js" 或 "test/**/*Spec.js"
输入空字符串进入下一个问题。 
>
(这里先留空,可根据测试情况灵活配置)>在符合匹配的文件中有哪些文件可以排除在外呢?
你可以使用通配符来匹配文件,比如:"**/*.swp"
输入空字符串进入下一个问题。 
> > 你想要Karma根据文件的变化立即做出响应么?
> yes

之后,你就会发现你的文件夹里多了一个文件:

e3829e78888ddc09bf25b58e2f20914b.png

打开这个文件,你会发现里面是一个配置项函数:

module.exports = function(config) {basePath: '', // 根路径将会同files和excluede项中的相对路径相关联frameworks: ['jasmine'], // 所使用的测试框架files: [], // 这里是需要测试的文件列表,有多种配置方式exclude: [], // 测试过程中排除在外的文件列表reporters: ['progress'], // 测试结果的汇报方式,port: 9876, // web服务器接口colors: true, // 是否使用彩色报告logLevel: config.LOG_INFO, // 日志级别,可配置的值有: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUGautoWatch: true, // 是否自动观测文档改变并执行测试命令browsers: ["Chrome", "PhantomJS"], // 用哪些浏览器测试呢singleRun: false, // 持续集成模式,如果设置成true,Karma将自行捕获浏览器,运行测试并根据结果退出,concurrency: Infinity // 并发数,同时跑多少个浏览器进行测试,默认无上限
}

默认会生成的配置项就是上面这些,更完整的配置请点我
这里稍微提一下browsers配置项,它可以配置高达8种浏览器:

a0711a9462d5e983db4fca5ef6af26f0.png

每一种都需要安装对应的launcher。其中有两个需要注意chromeHeadlessPhantomJS。这两个是无头浏览器。所谓无头浏览器就是没有脑袋的浏览器

8f843cffd46319c864c0fa9a31495b06.png
无头浏览器即headless browser,是一种没有界面的浏览器。既然是浏览器那么浏览器该有的东西它都应该有,只是看不到界面而已。因此这种浏览器没有渲染UI的过程,用于测试时的速度很快。

这就回答了上文launcher是啥的问题。毕竟,没有浏览器靠脑补可没法测试啊(真实)

言归正传。我们回到karma测试本身。接下来,我们修改一下配置:

files: ["src/srcTest/**/*.js", "test/unit/**/*.js"]

注意,上述写法只是配置写法中的一种, 配置的文件位置也是随您自己指定,更详细的配置请点我

采用上文写法的话,我们在files数组里面配置的第一项是需要测试的文件,第二项就是用什么方法去测试它的文件

因此,我们也在文件里创建对应的文件夹:

681f75e41cdf0cbffd7853eda5c100e3.png

这里有一个要注意的点。我们的需要测试的文件和测试驱动文件的名字是一一对应的,区别就在于测试驱动文件的名字后要加上.spec

那么我们就在srcTest的文件里面写点什么吧....

newBee.js

// 减法函数
function minus(x) {return function(y) {return x - y;};
}

testKarma.js

// 加法函数
function add(x) {return function(y) {return x + y;};
}// 乘法函数
function multi(x) {return function(y) {return x * y;};
}//if函数测试
function ifTest(boolean) {if (boolean) {return "热热";} else {return "凉凉";}
}// 反转字符串
function reverseStr (string) {return string.split("").reverse().join("");
}

e42d57c1abe155fd644142d1d98d87c3.png

那么接下来,就在.spec文件里写入对应的测试断言。我滴个龟龟,终于说到断言了。

因为我们这里使用的是Jasmine,因此就先放一下它的官网。

b140912442d13b35bc4aa484e3bdbcde.png
Global​jasmine.github.io

我们结合实例来说文档

newBee.spec.js

describe("newBee单元测试", function() {it("减法函数测试", function() {var minus7 = minus(7);expect(minus7(6)).toBe(0);});
});

testKarma.spec.js

describe("testKarma单元测试", function() {it("如果函数测试", function() {expect(ifTest(true)).toBe(true);expect(ifTest(false)).toBe("凉凉");});it("回文函数测试", function() {expect(reverseStr('abc')).toEqual('cba');})});

基本的格式就是这样的,下面来解释一下

// 分组describe(), 这个是可以嵌套的,并且每个单独的测试都有beforeAll, afterAll, beforeEach和afterEach
describe("这里写测试群组的名称", function(){// 具体的测试,it(), 当其中所有的断言都为true时,则通过;否则失效。it('这里写具体测试的名称', function(){var a = true;// 期望, expect()。 匹配,to*()// 每个匹配方法在期望值和实际值之间执行逻辑比较// 它负责告诉jasmine断言的真假,从而决定测试的成功或失败// 木有错,老子/老娘说啥就是啥expect(a).toBe(true); // 这是肯定断言expect(!a).not.toBe(true); // 这是否定断言// jasmine内置的匹配方法有很多,亦可自定义匹配方法// toBe()// toEqual()// toMatch()// toBeUndefined()// toBeNull()// toBeTruthy()// toContain()// toBeLessThan()// toBeCloseTo()// toThrowError()// 等等等等})
})

那么,测试方法写完了,我们来实际运行一下测试吧。打开终端,输入:

karma start

就会在终端看到

a331798a52cfbc9aa325a2d1610e1556.png

可以看到,我们的测试在Chrome和PhantomJS浏览器中分别测试了的5个方法,都有2个没有通过测试,没错,我们当初在写测试的时候故意写错了(这是真的)。

那么我们把测试修改成真值。

newBee.spec.js

describe("newBee单元测试", function() {it("减法函数测试", function() {var minus7 = minus(7);expect(minus7(6)).toBe(1);});
});

testKarma.spec.js

it("如果函数测试", function() {expect(ifTest(true)).toBe("热热");expect(ifTest(false)).toBe("凉凉");
});

结果是:

e1f5a64abbd3193f5d22f655d4d7e014.png

全部SUCCESS, 撒花。

到这里,一个基本的测试流程就走完了。然而,这并非终点。

其实,还能更进一步的。我们打开终端:

npm install karma-coverage --save-dev

然后打开karma.conf.js, 添加一些配置项

// 这里配置哪些文件需要统计测试覆盖率,例如,如果你的所有代码文件都在src文件夹中,你就需要如下配置
preprocessors: {"src/srcTest/*.js": "coverage"
},
// 新增coverageReporter选项
// 配置覆盖率报告的查看方式,type查看类型,可以取值html、text等等,dir输出目录
coverageReporter: {dir: "docs/unit",reporters: [{type: "html",subdir: "report-html"}]
},
reporters: ['progress', "coverage"] // 没错,reporters里面新增了一个coverage

然后保存,再运行一次karma start

接着会发现你的项目里多了一个文件夹

fda3d0530a10513497978f3c061695ca.png

用浏览器打开index.html。就会看到

5eaef53af2fe2fe2f5a8eaa0f351eda7.png

这就是你所写的js的测试覆盖率。

这样看起来是不是高大上了一些呢?

这里就有一个问题了。普通的js可以测试,可是我是写Vue的啊,Vue组件怎么测试呢?很简单,Vue官网有非常详细的测试教程。甚至还有专用的测试工具和测试说明

cb94a3739d107ef74b187ad70633c01c.png

彳亍口巴,你说的这些个单元测试看起来花里胡哨的,实际作用是什么呢?

单元测试的好处

  1. 单元测试不但会使你的工作完成得更轻松。而且会令你的设计会变得更好,甚至大大减少你花在调试上面的时间。
  2. 提高代码质量
  3. 减少bug, 快速定位bug
  4. 使修改和重构可以更放心
  5. 显得专业

单元测试的缺点

开发人员要花费时间在写测试代码上,然而又不会给你加工资...
小项目写测试只能单纯的增加开发时间和成本,然而又不会给你加工资...
我写了测试除了懂测试的人能看懂,别人又不知道,然而还不会给你加工资...

336b309ab9c1f65b93eec0660303c7e4.png

别别别,别打我...你先听我道(hu)理(jiao)讲(man)完(chan)。

  1. 对于所编写的代码,你在调试上面画了多少时间?
  2. 对于以前你自认为正确的代码,而实际上这些代码却存在重大的bug,你花了多少时间在重新确认这些代码上面?
  3. 对于一个别人报告的bug,你花了多少时间才找出导致这个bug的源码位置?

对于那些没有使用单元测试的程序员而言,上面这些问题所耗费的时间的是逐渐增加的,而且项目越深入,花费的时间越多;另一方面,适当的单元测试却可以很大程度地减少这些时间,从而为你腾出足够的时间来编写所有的单元测试——甚至可能还有剩余的空闲时间。

更加真实的是,主流的框架必须要写测试

不想当程序员的设计师不是好运维。 ----鲁迅

作为一个程序员,如果你想要让自己写的框架放到github和npm上能够为世界上的其他人所用。那么一个最基本的前提就是————代码没有BUG。可是,你的怎么向语言不通思维不同的人解释你的JavaScript库确实足够健壮呢。这个时候就需要单元测试出场了。

主流前端框架虽然在所使用的测试库(karma、jest、QUnit)和断言库(assert、jasmine、 chai)上略有差别,但Vue、React、Angular、Underscore甚至是jQuery都写了单元测试。

来个石锤

e6fec5e1fcb0d96f7e0ee8695388000c.png

下面我们看一看Vue的测试是怎么写的:

git clone https://github.com/vuejs/vue.git
npm install
npm run test unit // 这里可以看到单元测试
npm run test // 这里就看全部的测试

Vue的测试覆盖率为

57e5636d86ffbb015c0c9af45830dc65.png

举例:v-show的测试

// import Vue from 'vue'describe('Directive v-show', () => {it('should check show value is truthy', () => {const vm = new Vue({template: '<div><span v-show="foo">hello</span></div>',data: { foo: true }}).$mount()expect(vm.$el.firstChild.style.display).toBe('')})it('should check show value is falsy', () => {const vm = new Vue({template: '<div><span v-show="foo">hello</span></div>',data: { foo: false }}).$mount()expect(vm.$el.firstChild.style.display).toBe('none')})it('should update show value changed', done => {const vm = new Vue({template: '<div><span v-show="foo">hello</span></div>',data: { foo: true }}).$mount()expect(vm.$el.firstChild.style.display).toBe('')vm.foo = falsewaitForUpdate(() => {expect(vm.$el.firstChild.style.display).toBe('none')vm.foo = {}}).then(() => {expect(vm.$el.firstChild.style.display).toBe('')vm.foo = 0}).then(() => {expect(vm.$el.firstChild.style.display).toBe('none')vm.foo = []}).then(() => {expect(vm.$el.firstChild.style.display).toBe('')vm.foo = null}).then(() => {expect(vm.$el.firstChild.style.display).toBe('none')vm.foo = '0'}).then(() => {expect(vm.$el.firstChild.style.display).toBe('')vm.foo = undefined}).then(() => {expect(vm.$el.firstChild.style.display).toBe('none')vm.foo = 1}).then(() => {expect(vm.$el.firstChild.style.display).toBe('')}).then(done)})it('should respect display value in style attribute', done => {const vm = new Vue({template: '<div><span v-show="foo" style="display:block">hello</span></div>',data: { foo: true }}).$mount()expect(vm.$el.firstChild.style.display).toBe('block')vm.foo = falsewaitForUpdate(() => {expect(vm.$el.firstChild.style.display).toBe('none')vm.foo = true}).then(() => {expect(vm.$el.firstChild.style.display).toBe('block')}).then(done)})it('should support unbind when reused', done => {const vm = new Vue({template:'<div v-if="tester"><span v-show="false"></span></div>' +'<div v-else><span @click="tester=!tester">show</span></div>',data: { tester: true }}).$mount()expect(vm.$el.firstChild.style.display).toBe('none')vm.tester = falsewaitForUpdate(() => {expect(vm.$el.firstChild.style.display).toBe('')vm.tester = true}).then(() => {expect(vm.$el.firstChild.style.display).toBe('none')}).then(done)})
})

只要你的测试覆盖率足够高,你就可以在著名的GitHub装逼网站Codecov搞一个覆盖率标签了。就像下面这个:

ae735767b79d0f15a5ee18c5632dbb90.png

怎么样,这样你所写的框架,是不是就逼格满满?

02843ef8049471bce64ebb118f96c601.png

所以你还在等什么,测不了吃亏,测不了上当,赶紧在自己的代码中加入测试吧,~~只要998~~,代码逼格带回家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

excel 行高 上下留白_拒绝加班,工作中最常用的57个Excel小技巧来了!

今天高顿君分享的 Excel小技巧&#xff0c;全是工作是最常用且简单易操作的&#xff0c;共57个&#xff0c;希望对同学们有所帮助。&#xff08;适合版本 Excel2007及以上&#xff09;一、文件操作1、为excel文件添加打开密码文件 - 信息 - 保护工作簿 - 用密码进行加密。2、为…

经验分享:三步走教你升级企业NAS设备

前几年凡是对于数据存储有需求的企业都已经购买了相关的NAS产品&#xff0c;不过电脑和网络升级换代是比较频繁的&#xff0c;几年过去了中小企业对数据存储的需求也水涨船高&#xff0c;然而面对当初的NAS存储设备该如何处理呢&#xff1f;扔掉可惜使用又不如意的鸡肋问题能够…

获取访客进站关键词_拼多多访客突然下降是为什么?拼多多访客突然暴涨又是怎么回事?...

在当下这个互联网时代&#xff0c;可以说流量就代表这金钱。这一点在做电商的商家那里表现的就更为直观了&#xff0c;如果你做了一个拼多多的店铺&#xff0c;之前店铺的流量一直都比较好&#xff0c;而现在拼多多店铺的流量忽然下降了&#xff0c;那么店铺中的销售额就会受到…

通过Matlab实现离散序列卷积和

前言 年轻人&#xff0c;你对数学一无所知&#xff0c;你只是习惯了而已。—冯诺伊曼 Young man, in mathematics you dont understand things. You just get used to them.—John von Neumann。 一、卷积和是什么&#xff1f; 卷积的本质是描述一个瞬时动作&#xff08;激励…

mac电脑下Tomcat和Apach配置流程(超详细)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 本章介绍在mac 电脑下如何配置Tomcat、Apach等环境 一、Apache介绍及配置 1.XAMPP安装 为了更好的进行各项软件服务的配置&#xff0c;引入快捷脚本工具——XMAPP。…

cvc 降噪_耳机降噪功能这么多,说说什么是ANC、ENC、CVC、DSP降噪

降噪功能对耳机的作用很重要&#xff0c;一是减少噪音&#xff0c;避免过度放大音量&#xff0c;从而减少对耳朵的损害。二是过滤噪音从而提高音质和通话质量。降噪可分为被动式降噪和主动式降噪。被动式降噪也就是物理降噪&#xff0c;被动式降噪是指利用物理特性将外部噪声与…

RPC 和 RESTful

2019独角兽企业重金招聘Python工程师标准>>> to do ... 转载于:https://my.oschina.net/u/2002769/blog/1505410

密码学入门1——凯撒密码和三重DES加解密

实验目的 1、完成第一个入门加解密——凯撒密码 2、完成当下较为流行的三重DES加解密技术 3、熟悉所学的实际运用方向 实验准备 硬件&#xff1a;计算机或笔记本电脑 操作系统&#xff1a;Mac操作系统 IDE环境&#xff1a;Eclipse 程序语言&#xff1a;Java 一、实验基本…

emqx 使用端口_数据传输、存储、展现,EMQ X + TDengine 搭建 MQTT 物联网数据可视化平台...

物联网数据采集涉及到大量设备接入、海量的时序数据传输&#xff0c;EMQ X 消息中间件与 TDengine 大数据平台的组合技术栈完全能够胜任场景中的海量时间序列监测数据的传输、存储和计算。数据入库后&#xff0c;往往需要其他方式如数据可视化系统将数据按照规则统计、展现出来…

python字符串去头尾_带你认识优秀的python代码

有一串长的字符串names "LI XIA , ZHAO MING ,LAO WANG *,DA XIONG >,LI MEI MEI, CHANG JIANG,LI QIANG,ZHANG WU JI,ZHANG SAN FENG,DU GU QIU BAI,QIAO FENG"。要求&#xff1a;&#xff08;1&#xff09;过滤所有的名字&#xff0c;去掉每个名字左右的空格和…

python学习笔记之装饰器、递归、算法(第四天)

参考老师的博客&#xff1a; 金角&#xff1a;http://www.cnblogs.com/alex3714/articles/5161349.html 银角&#xff1a;http://www.cnblogs.com/wupeiqi/articles/4963027.html 一、冒泡算法实例&#xff1a; a [32,5,22,41,7,31,12,102,74,37,9,25] 1、方法1&#xff1a; c…

数字信号处理笔记1-信号与常见操作

年轻人&#xff0c;你对数学一无所知&#xff0c;你只是习惯了而已。 —冯诺伊曼 前言 本学期开始跟着实验室学习计算机视觉领域&#xff0c;而一个重要的基础知识就是《数字图像处理》&#xff0c;而数字信号处理作为一个大类&#xff0c;可以将数字图像处理理解为数字信号处…

[linux驱动]linux驱动模块

一&#xff0c;内核模块的概念 经常在内核驱动代码看到类似fs_init()等驱动初始化函数&#xff0c;那么这个和module_init()函数的差别在哪里&#xff0c;宏定义__define_initcall(level,fn)对于内核的初始化很重要&#xff0c;他指示编译器在编译的时候&#xff0c;将一系列初…

看我如何基于PythonFacepp打造智能监控系统

由于种种原因&#xff0c;最近想亲自做一个基于python&facepp打造的智能监控系统。 000&#xff1a;萌芽 1&#xff1a;暑假在家很无聊 想出去玩&#xff0c;找不到人。玩个lol&#xff08;已卸载&#xff09;&#xff0c;老是坑人。实在是无聊至极&#xff0c;不过&#x…

c++ 遍历所有点且距离最短_编程小白暑期进阶笔记41-C语言数据结构与算法图遍历的应用...

基于广度优先遍历算法的应用思考题&#xff1a;&#xff08;思考题答案&#xff1a;BFS(广度优先遍历)在一般的带权图中是不能解决最短路问题&#xff0c;了解BFS的都知道&#xff0c;BFS是根据节点到源节点之间的节点数遍历的&#xff0c;也就是先访问离源节点节点数最少的点。…

关于@WebServlet(“LoginServlet“)404 报错的解决办法 “请求的资源[/test/LoginServlet] 不可用”

关于WebServlet&#xff08;“LoginServlet”&#xff09;404 报错的解决办法 “请求的资源[/test/LoginServlet] 不可用” *一切事物的开头总是困难这句话&#xff0c;在任何一种科学上都是适用的。 * ——马克思 一个困扰了我n天的问题&#xff0c;终于终于还是解决了&#…

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(14)-主框架搭建

ASP.NET MVCEF框架EasyUI实现权限管理系列(14)-主框架搭建 原文:ASP.NET MVCEF框架EasyUI实现权限管理系列(14)-主框架搭建ASP.NET MVCEF框架EasyUI实现权限管系列 (开篇) (1):框架搭建 (2):数据库访问层的设计Demo (3):面向接口编程 (4 ):业务逻辑层的封装 (5):前台…

常用事务代码 sap_SAP_PS_事务代码

[转]SAP PS常用事务代码T-CODESAP项目系统(Project System&#xff0c;以下简称PS)模块作为传统的非常规模块(除FI、CO、MM、PP、SD之外的模块)之一&#xff0c;在最近几年在国内也得到的较为广泛的应用,与PS应用火热场景相对应的是PS内外部顾问的极度缺乏。这种缺乏一方面表现…

CLion for mac安装配置

前言 本文详细多图介绍 IntelliJ IDEA For Mac的激活教程&#xff0c;相当于永久激活 文件包百度云下载&#xff1a;(通过与熊论道网站解密&#xff09; 熊曰&#xff1a;呋溫捕嘿誘襲氏樣溫住既非破哮誒襲非捕溫肉性盜森魚非襲啽蜜呦訴嘿溫類盜山寶住出森非喜誘捕發嗥既肉嗅…

功率信号与能量信号的超棒理解!

功率信号与能量信号的理解&#xff01; 功率信号和能量信号一直是一个令我疑惑的概念&#xff0c;一个无限一个为零。但是下面令我茅塞顿开&#xff01; &#xff5e;&#xff5e;&#xff5e;分割线啊分割线&#xff5e;&#xff5e;&#xff5e;