Mocha和单元测试

Mocha测试框架和项目实例

测试项目已上传github

地址:https://github.com/Hanxueqing/Mocha-test

运行克隆命令,下载到本地

git clone git@github.com:Hanxueqing/Mocha-test.git

一、什么是Mocha

Mocha(发音"摩卡")诞生于2011年,是现在最流行的JavaScript测试框架之一,在浏览器和Node环境都可以使用。所谓"测试框架",就是运行测试的工具。通过它,可以为JavaScript应用添加测试,从而保证代码的质量。

二、Mocha安装和准备

前期准备:安装vue-cli脚手架、安装node、安装Git

(1)先用vue-cli创建一个vue-test项目

vue create vue-test

(2)全局安装mocha

npm i mocha -g

将mocha安装到本地目录

npm i mocha -D

(3)修改package.json中的test路径为我们本地的mocha路径

"test": "./node_modules/mocha/bin/mocha"
image

三、测试框架

1、创建test文件夹

测试脚本都存放在test文件夹中

2、编写测试框架

创建test/demo.js

describe("Demo", function(){
    describe("方法 1", function(){
        context("情境 1", function(){
            before(function(){
                console.log("-----测试之前------");
            });
            after(function(){
                console.log("-----测试之后------");
            });
            beforeEach(function(){
                console.log("-------每条测试之前---------");
            })
            afterEach(function(){
                console.log("-------每条测试之后---------");
            })
            it("测试 1", function(){

            })
            it("测试 2", function(){
                
            })
        })
    })
})

上面这段代码,就是测试脚本,它可以独立执行。测试脚本里面应该包括一个或多个describe块,每个describe块应该包括一个或多个it块。

describe

describe块称为"测试套件"(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称("Demo"),第二个参数是一个实际执行的函数。

it

it块称为"测试用例"(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称("测试1"),第二个参数是一个实际执行的函数。

测试用例的钩子

Mocha在describe块之中,提供测试用例的四个钩子:before()after()beforeEach()afterEach()。它们会在指定时间执行。

befor:定义测试之前进行的操作

after:定义测试之后进行的操作

beforeEach:定义每条测试之前进行的操作

afterEach:定义每条测试之后进行的操作

3、进行测试

执行mocha命令使用全局安装的mocha进行测试或者执行npm test命令使用本地安装的mocha进行测试

image

四、断言库chai

所谓"断言",就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。

1、全局安装chai模块

npm i chai -g

2、本地安装chai模块

npm i chai -D

3、断言风格

断言库有很多种,Mocha并不限制使用哪一种。

assert风格的断言

在test文件夹下新建test_lib文件夹,创建assert.js编写测试脚本。

const chai = require("chai");
//引入断言的风格
const assert = chai.assert;

describe("Demo", function () {
    it("使用 assert风格的断言测试", function () {
        var value = "hello";
        //断言value值的类型为字符串
        assert.typeOf(value, "string");
        //断言value值等于"hello"
        assert.equal(value, "hello");
        //断言value值的长度为5
        assert.lengthOf(value, 5);
    })
})

进入test_lib文件夹下,执行mocha assert.js运行测试脚本,因为我们这三个断言都是真的,所以测试通过。

image
should风格断言

创建should.js编写测试脚本。

const chai = require("chai");
const should = chai.should();

describe("Demo", () => {
    it("使用 should风格的断言测试", function () {
        var value = "hello";
        //value应该存在
        value.should.exist
        //value的数字类型应该是一个字符串
        value.should.be.a("string");
        //value值应该等于"hello"
        value.should.equal("hello");
        //value值不等于"你好"
        value.should.not.equal("你好");
        //value的长度应该为5
                value.should.have.length(5);
        

    })
})

我们也可以换成另一种更简洁的写法,使用and连接断言

value.should.exist
            .and.be.a("string")
            .and.equal("hello")
            .and.have.length(5)

进入test_lib文件夹下,执行mocha should.js运行测试脚本,因为我们这五个断言都是真的,所以测试通过。

image
expect风格断言

expect断言的优点是很接近自然语言

创建expect.js编写测试脚本。

const chai = require("chai");
const expect = chai.expect;

describe("Demo", () => {
    it("使用 expect风格的断言测试", function () {
        var value = "hello";
        //value应该存在
        expect(value).to.exist;
        //value的数字类型应该是一个字符串
        expect(value).to.be.a("string");
        //value值应该等于"hello"
        expect(value).to.equal("hello");
        //value值不等于"你好"
        expect(value).to.not.equal("你好");
        //value的长度应该为5
        expect(value).to.have.length(5);
    })
})

进入test_lib文件夹下,执行mocha expect.js运行测试脚本,因为我们这五个断言都是真的,所以测试通过。

image

同样我们也可以定义一个数字,使用expect断言来判断数字的区间

var number = 3;
        //判断number是否在3~5之间的数
        expect(number).to.be.at.most(5);
        expect(number).to.be.at.least(3);
        //判断number是否在1~3之间的数
        expect(number).to.be.within(1, 3);

五、编写测试脚本

Mocha的作用是运行测试脚本,首先必须学会写测试脚本。所谓"测试脚本",就是用来测试源码的脚本。

1、测试返回结果是否正确

(1)创建被测试的项目lib/demo-1.js

class Demo {
    subtotal(unitPrice, quantity) {
        return unitPrice * quantity;
    }
}
module.exports = Demo;

(2)创建测试脚本test/demo-1.test.js

通常,测试脚本与所要测试的源码脚本同名,但是后缀名为.test.js(表示测试)或者.spec.js(表示规格)。比如,demo-1.js的测试脚本名字就是demo-1.test.js

//demo-1.test.js
const chai = require("chai");
const expect = chai.expect;

var Demo = require("../lib/demo-1");
var demo = new Demo();
describe("Demo",()=>{
    it("单价10块钱的3件商品小计金额应该是30块",function(){
        var subtotal = demo.subtotal(10,3);
        expect(subtotal).to.equal(30);
    })
})

2、异步操作测试setTimeout

(1)Mocha默认每个测试用例最多执行2000毫秒,如果到时没有得到结果,就报错。对于涉及异步操作的测试用例,这个时间往往是不够的,需要用-t--timeout参数指定超时门槛。

进入lib/demo-1.js,编写异步等待方法,规定2秒之后返回结果

waitTwoSecond(data,callback){
        setTimeout(function(){
            callback(data);
        },2000);
    }

(2)进入test/demo-1.test.js,编写测试脚本

//异步操作测试
    //mocha不会等到异步执行结束以后进行测试,而是直接运行得到测试结果
    it("一段时间以后返回数据",function(done){
        demo.waitTwoSecond("hello",function(data){
            expect(data).to.equal("hello")
            done(); //只有调用done方法才能等待调用结束以后测试
            //mocha默认的等待时间是2秒,上述操作超过两秒,报错
            //运行命令mocha demo-1.test.js -t 5000重置等待时间解决
        })
    })

另外,上面的测试用例里面,有一个done函数。it块执行的时候,传入一个done参数,当测试结束的时候,必须显式调用这个函数,告诉Mocha测试结束了。否则,Mocha就无法知道,测试是否结束,会一直等到超时报错。如果把这行删除,则mocha不会等到异步执行结束以后进行测试,而是直接运行得到测试结果,返回的断言结果始终为真。

(3)虽然测试用例中规定2秒返回结果,但是实际运行时间肯定超过2秒,所以,需要用-t--timeout参数,改变默认的超时设置。

mocha demo-1.test.js -t 5000

上面命令将测试的超时时限指定为5000毫秒。

3、测试接口数据https

接口地址https://douban.uieee.com/v2/movie/top250

image

(1)lib/demo-1.js

引入https模块

var https = require("https");

定义fetchData方法

fetchData(api,callback){
        var requestUrl = `https://douban.uieee.com/v2/movie/${api}`;
        https.get(requestUrl,function(res){
            var responseData = ""
            res.setEncoding("utf8")

            res.on("data",function(chunk){
                responseData += chunk
            })

            res.on("end",function(){
                callback(JSON.parse(responseData))
            })
        })
    }

(2)test/demo-1.test.js

it("加载豆瓣api,返回的数据,应该包含subjects属性",function(done){
        demo.fetchData("top250",function(data){
            expect(data).to.have.property("subjects");
            done();
        })
    })

    it("加载豆瓣api,返回的数据,subjects应为对象类型", function (done) {
        demo.fetchData("top250", function (data) {
            var subjects = data.subjects;
            expect(subjects).to.be.a("array");
            done();
        })
    })

    it("加载豆瓣api,返回的数据,subjects长度应为20", function (done) {
        demo.fetchData("top250", function (data) {
            var subjects = data.subjects;
            expect(subjects).to.have.length(20);
            done();
        })
    })

    it("加载豆瓣api,返回的数据,title属性应该是字符串类型的", function (done) {
        demo.fetchData("top250", function (data) {
            var title = data.subjects[0].title
            expect(title).to.be.a("string");
            expect(title).to.equal("肖申克的救赎")
            done();
        })
    })

(3)运行结果

image

4、测试异常

(1)lib/demo-1.js

engine(fuel){
        if(fuel !== "gas"){
            throw new Error("not accept")
        }
    }

(2)test/demo-1.test.js

//定义一个异常
    it("给汽车引擎加水是不能接受的事情",function(){
        expect(function(){
            demo.engine("water");
        }).to.throw("not accept")
    })
//另外一种写法
    it("给汽车引擎加水是不能接受的事情",function(){
        expect(demo.engine.bind(demo,"water")).to.throw("not accept")
    })

(3)测试结果

image

六、运行多个测试

1、直接使用mocha命令运行test子目录中的测试脚本

Mocha默认运行test子目录里面的测试脚本。所以,一般都会把测试脚本放在test目录里面,然后执行mocha就不需要参数了。

image

所以在控制台中输入mocha,只会执行test子目录里的测试脚本demo-1.test.js和demo.js,而test_lib中的assert.js、expect.js、should.js则不会执行。

运行结果:

image

2、执行test子目录下面所有的测试用例

这时可以看到,test子目录里面的测试脚本执行了。但是,你打开test子目录,会发现下面还有一个test/test_lib子目录,里面还有三个测试脚本assert.js、expect.js、should.js,并没有得到执行。Mocha默认只执行test子目录下面第一层的测试用例,不会执行更下层的用例。

为了改变这种行为,就必须加上--recursive参数,这时test子目录下面所有的测试用例,不管在哪一层,都会执行。

mocha --recursive

运行结果:

image

3、执行多个测试脚本

mocha命令后面紧跟测试脚本的路径和文件名,可以指定多个测试脚本。

进入tets_lib目录下,运行assert.js和should.js两个测试脚本

mocha assert.js should.js

运行结果

image

七、测试用例管理

1、only表示只运行某个测试套件或测试用例。

大型项目有很多测试用例。有时,我们希望只运行其中的几个,这时可以用only方法。describe块和it块都允许调用only方法,表示只运行某个测试套件或测试用例。

it.only("单价10块钱的3件商品小计金额应该是30块",function(){
        var subtotal = demo.subtotal(10,3);
        expect(subtotal).to.equal(30);
    })

运行结果:只运行了添加only方法的测试脚本

image

2、skip表示跳过指定的测试套件或测试用例。

it.skip("一段时间以后返回数据",function(done){
        demo.waitTwoSecond("hello",function(data){
            expect(data).to.equal("hello")
            done(); //只有调用done方法才能等待调用结束以后测试
            //mocha默认的等待时间是2秒,上述操作超过两秒,报错
            //运行命令mocha demo-5.js -t 5000重置等待时间解决
        })
    })

运行结果:跳过了添加skip方法的测试脚本

image

八、ES6测试

1、ES5写法

src/add.js

function add(x, y) {
    return x + y;
}

module.exports = add;

test/demo-2.test.js

const chai = require("chai");
const expect = chai.expect;
var Add = require("../src/add.js");

describe('加法函数的测试', function () {
    it('1 加 1 应该等于 2', function () {
        expect(Add(1, 1)).to.be.equal(2);
    });
});

运行命令

mocha

运行结果

image

2、ES6写法

test/demo-2.test.js

const chai = require("chai");
const expect = chai.expect;
var Add = require("../src/add.js");

describe('加法函数的测试', function () {
    it('1 加 1 应该等于 2', function () {
        expect(Add(1, 1)).to.be.equal(2);
    });
});

如果测试脚本是用ES6写的,那么运行测试之前,需要先用Babel转码。

(1)安装Babel

npm install babel-core babel-preset-es2015 --save-dev

(2)在项目目录下面,新建一个.babelrc文件:

{
  "presets": [ "es2015" ]
}

运行命令

./node_modules/mocha/bin/mocha --require babel-core/register

运行结果

image

九、测试报告

运行mocha --reporters可以显示所有内置的报告格式。

image

1、spec格式

--reporter参数用来指定测试报告的格式,默认是spec格式。

mocha
等同于
mocha --reporter spec

运行结果:

image

2、tap格式

mocha --reporter tap

运行结果:

image

3、HTML格式

使用mochawesome模块,可以生成漂亮的HTML格式的报告。

(1)安装mochawesome模块

npm install --save-dev mochawesome

(2)执行测试命令

./node_modules/.bin/mocha --reporter mochawesome

上面代码中,mocha命令使用了项目内安装的版本,而不是全局安装的版本,因为mochawesome模块是安装在项目内的。

(3)运行结果:

image

(4)测试结果报告在mochaawesome-reports子目录生成。

image

(5)在浏览器中浏览html格式的测试报告

image

十、生成测试文件

Mocha支持从测试用例生成规格文件。

1、Markdown格式

mocha demo-1.test.js --recursive -R markdown > spec.md

上面命令根据test目录的demo-1.test.js测试脚本,生成一个规格文件spec.md-R markdown参数指定规格报告是markdown格式。

image

2、HTML格式

mocha demo-1.test.js --recursive -R doc > spec.html

上面命令根据test目录的demo-1.test.js测试脚本,生成一个规格文件spec.html.

image

十一、在浏览器中测试

除了在命令行运行,Mocha还可以在浏览器运行。

1、首先,使用mocha init命令在指定目录生成初始化文件。

mocha init vue-test

运行上面命令,就会在vue-test目录下生成index.html文件,以及配套的脚本和样式表。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Mocha</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="mocha.css">
  </head>
  <body>
    <div id="mocha"></div>
    <script src="mocha.js"></script>
    <script>mocha.setup('bdd');</script>
    <script src="tests.js"></script>
    <script>
      mocha.run();
    </script>
  </body>
</html>

2、新建一个源码文件add.js

function add(x, y) {
    return x + y;
}

module.exports = add;

3、新建一个测试脚本tests.js

var expect = chai.expect;

describe('加法函数的测试', function () {
    it('1 加 1 应该等于 2', function () {
        expect(add(1, 1)).to.be.equal(2);
    });

    it('任何数加0等于自身', function () {
        expect(add(1, 0)).to.be.equal(1);
        expect(add(0, 0)).to.be.equal(0);
    });
});

4、然后,把这个文件,以及断言库chai.js,加入index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Mocha</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="mocha.css">
  </head>
  <body>
    <div id="mocha"></div>
    <script src="mocha.js"></script>
    <script>mocha.setup('bdd');</script>
    <script src="add.js"></script>
    <script src="http://chaijs.com/chai.js"></script>
    <script src="tests.js"></script>
    <script>
      mocha.run();
    </script>
  </body>
</html>

5、现在,在浏览器里面打开index.html,就可以看到测试脚本的运行结果。

image

参考文档

mocha官网

https://mochajs.org/

测试框架 Mocha 实例教程 by 阮一峰

https://juejin.im/entry/5941ea698d6d810058bff709

【前端单元测试入门01】Mocha与chai

https://www.jianshu.com/p/aa53ac34e4c0

vue项目中添加单元测试

https://blog.csdn.net/weixin_33739523/article/details/92436029

vue官网-单元测试模块

https://cn.vuejs.org/v2/guide/unit-testing.html#ad

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容