單元測試
前端測試框架主要是Mocha與Jasmine,這里我們選擇Mocha,斷言庫有should、expect、chai以及node自帶的assert。這里我們選擇chai,chai中包含了expect、should及assert的書寫風格。
關于構建這一塊給大家推薦一篇:前端測試環境搭建;
這里我們重點介紹 mocha 和 chai 的語法和用法
如果安裝過程中報錯,嘗試清除下node_modules重新安裝依賴 (mac下可能會報一些錯 不過問題不大 ~~)
mocha
- 基本用法 (這里默認大家搭建好了環境 直接上代碼~~)
const getNum = (value) => {
if(value === 0) {
return 1
}else {
return value * 2
}
}
describe('Test', function() {
it('should return 20 when the value is 10', function() {
expect(getNum(10)).to.equal(20)
})
it('should return 1 when the value is 0', function() {
expect(getNum(0)).to.equal(0)
})
})
- 鉤子函數
before(function () {
createDiv('test')
})
beforeEach(function() {
console.log('即將測試 ^_^');
})
afterEach(function() {
console.log('測試結束 準備下一個測試 ^_^');
})
after(function() {
console.log('Tests所有測試結束 ^_^');
})
- 幾個常用的方法介紹
//only
it.only('only run this',function() {
// setTimeout(done, 300); //來判定是否超時
console.log('只會執行這個'); // only 方法 可以給多個子例綁定only 方法
})
})
//skip 和 retries
it.skip('i will skip ',function() {
console.log('可以不用注釋這段代碼哦') // skip方法用于跳過指定用例
})
it.only('如果不滿足條件 我就跳過這個用例',function() {
// setTimeout(done, 300);
this.retries(2); // 失敗后 繼續執行 2次
// console.log('繼續嘗試測試 執行中...')
if(false) {
console.log('條件滿足 正常測試')
}else{
this.skip(); // 跳過該測試
}
})
//timeout
beforeEach(function(done) { //如果這段代碼很耗時 下面的測試也會影響到 如果不想引入 done參數也要去掉
// this.timeout(3000); // A very long environment setup.
//setTimeout(done, 2500);
console.log('即將測試 ^_^');
})
//循環任務
function add() {
return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {
return prev + curr;
}, 0);
}
describe('add()', function() {
after(function() {
console.log('ADD所有測試結束 ^_^');
})
var tests = [
{args: [1, 2], expected: 3},
{args: [1, 2, 3], expected: 6},
{args: [1, 2, 3, 4], expected: 10}
];
tests.forEach(function(test) {
it.only('correctly adds ' + test.args.length + ' args', function() { // 如果前面的測試單元用了only方法這里也是不會被測試到的 所以也要加only
var res = add.apply(null, test.args);
expect(res).to.equal(test.expected)
});
});
});
const qual = function(val) {
return val*2;
}
chai
- 以下是chai常用的一些斷言語法
// not
it.only("鏈式",function() {
expect(qual(10)).to.equal(20); // 肯定
expect(qual(11)).to.not.equal(20); //取反
});
// any 和 all
let foos = {bar:'baz',baz:'baa'};
expect(foos).to.have.any.keys('bar','baz'); // any 期望有一個這樣 key鍵
expect(foos).to.have.all.keys('bar','baz'); // all 期望全部都是
// a 或an 測試值類型
expect('test').to.be.a('string');
expect(foos).to.be.an('object');
expect(null).to.be.a('null');
expect(undefined).to.be.a('undefined');
expect(new Error).to.be.an('error');
//include 包含斷言
expect([1,2,3]).to.include(2);
expect({a:1,b:2}).to.include.keys('a');
//ok 斷言目標為真
expect('1').to.be.ok;
//true 不會對類型進行轉換
expect('1').to.be.not.true;
//false 不會對類型進行轉換
expect('1').to.be.not.false;
//null undefined
expect(null).to.be.null;
expect(undefined).to.undefined;
//NAN
expect('1').to.be.NAN;
//exist 非null 非undefined
let c = null,d = 1;
expect(c).to.be.not.exist;
expect(d).to.be.exist;
//empty 判斷目標長度 為空 length
expect({c:1}).to.not.empty;
//arguments
function test1() {
expect(arguments).to.arguments;
}
//equal(value) 嚴格等于
expect(1+1).to.equal(2);
expect({foo:'a'}).to.not.equal({foo:'a'}); // 不嚴格等于 對象地址不同
expect({foo:'a'}).to.deep.equal({foo:'a'}); // 帶有deep標記的 比較的是實際的value
//eql(value) 相當于深度比較
expect({foo:'a'}).to.eql({foo:'a'});
//above 大于
expect(3).to.above(1);
//可以配合length使用
expect('hahha').to.have.length.above(1);
//least 大于等于
expect(2).to.least(2);
//below 小于
expect(2).to.below(4);
//most 小于等于
expect(4).to.most(5);
//within 測試區間內
expect(4).to.within(1,6);
let inst = function() {} , inst_ = new inst();
//instanceof(constructor) 判斷實例是否屬于構造函數
expect(inst_).to.instanceof(inst);
//property(name,value)
expect({foo:'baz'}).to.have.property('foo','baz');
//ownProperty(name) 判斷自身屬性
expect('strings').to.have.ownProperty('length');
//.length
//lengthOf(value) 期待的length值
expect('111').to.have.lengthOf(3);
//match(reg) 匹配正則
expect('wahaha').to.match(/^wa/);
//string(value) 判斷字符串包含另一個字符串
expect('wahaha').to.have.string('haha');
//keys
//throw 斷言出錯
var fn = function () { throw err }
expect(fn).to.throw(ReferenceError);
//respondTo(method) 斷言目標對象下有該方法
inst.prototype.test = function() {};
expect(inst).to.respondTo('test');
//掛載itself方法 對象自身方法 而不是公共的
expect(inst).itself.to.not.respondTo('test');
//satisfy(fn) 給測試目標傳入函數 返回期望值
expect(2).to.satisfy(function(num) { return num > 1 });
//closeTo(expect,范圍)
expect(1.5).to.closeTo(1,0.8); //期望是1,但是只要在0.8范圍內就好
//members(set) 斷言集合
expect([1,2,3]).to.have.members([3,2,1]);
expect([1,2]).to.have.include.members([1]);
//oneOf 斷言位置
expect(1).to.oneOf([1,2,3]);
//change(object, property) 斷言目標方法會改變指定對象的指定屬性
let fn1 = { val:2 };
let fn2 = function() { fn1.val++; };
expect(fn2).to.change(fn1,'val'); //期待fn2能夠改變fn1的屬性val的值
//increase (object, property) 斷言目標方法會增加指定對象的屬性
let fn3 = { val:2 };
let fn4 = function() { fn3.val++; };
expect(fn4).to.increase(fn3,'val');
// decrease(object, property) 斷言目標方法會減少指定對象的屬性
let fn5 = function() { fn3.val--; };
expect(fn5).to.decrease(fn3,'val');
// .extensible 斷言目標對象是可擴展的(可以添加新的屬性) .sealed 封閉
expect({}).to.be.extensible;
//.frozen 斷言目標對象是凍結的(無法添加新的屬性并且存在的屬性不能被刪除和修改)
var frozenObject = Object.freeze({})
expect(frozenObject).to.be.frozen
expect({}).to.not.be.frozen
})
})