1.assert断言模块主要解决:
- 测试需求,提供校验值是否为真、相等/不相等、深度匹配/不匹配等调试方法,不满足校验条件的通过fail函数构造AssertionError对象并抛出。
- 抛出错误,构造执行函数block,通过block函数抛出错误对象Error,捕获该错误对象,与期望值比较,assert.throw方法中若由block捕获的错误匹配expected期望的校验要求(包括正则、构造函数、以block捕获错误为参数的函数),则既不抛出Error错误,也不抛出AssertionError错误;assert.doesNotThrow方法,若block捕获错误符合期望,抛出AssertionError错误,若不符合,抛出Error错误。
2.assert一般测试方法的构建:
- 通过assert.throw(block[,error][,message])方法校验block函数执行时捕获的错误是否符合期望error的校验条件,因此该测试方法的实现在于构造block函数。或者重新构建assert方法
3.与jquery-unit的比较:
- assert模块相等性比较更为细致,还需顾及buffer、ArrayBuffer、DataView对象。
- 添加一般测试方法,jquery-validate提供了定制校验方法的功能,可以用于多个校验框,jquery-unit也能构造特定的通用的校验方法用于多个测试对象,assert需要反复重写block函数构建,或者重新构建assert方法。
4.使用
- assert.AssertionError(options) 配置AssertionError错误对象
- assert.fail(actual, expected, message, operator) 配置AssertionError错误对象,并抛出该错误,actual为实际值,expected为期望值,message错误消息,operater分隔符
- assert(value, message), assert.ok(value, [message]) 校验value是否为真
- assert.equal(actual, expected, [message]) 校验两者是否相等==
- assert.notEqual(actual, expected, [message]) 校验两者是否不相等!=
- assert.deepEqual(actual, expected, [message]) 数组、对象、时间对象、正则对象、ArrayBuffer对象、arguments对象深度匹配,非严格模式
- assert.deepStrictEqual(actual, expected, [message]) 数组、对象、时间对象、正则对象、ArrayBuffer对象、arguments对象深度匹配,严格模式
- assert.notDeepEqual(actual, expected, [message]) 深度匹配返回否值,非严格模式
- assert.notDeepStrictEqual(actual, expected, [message]) 深度匹配返回否值,严格模式
- assert.strictEqual(actual, expected, [message]) 相等比较===
- assert.notStrictEqual(actual, expected, [message]) 不相等比较!==
- assert.throws(block, [error], [message]) block没有错误抛出AssertionError错误,或者错误不符期望,抛出block捕获的错误
- assert.doesNotThrow(block, [message]) 没有期望或者block捕获错误符合期望,抛出AssertionError错误;block捕获错误不符合期望,抛出block捕获的错误
- assert.ifError(value) 参数为Error对象,直接抛出该错误对象
5源码:
'use strict'; // 比较字符串、buffer对象 function compare(a,b){ if ( a===b ){ return 0; } var x=a.length; var y=b.length; for ( var i=0, len=Math.min(x, y); i<len; ++i ){ if ( a[i]!==b[i] ){ x=a[i]; y=b[i]; break; } } if ( x<y ){ return -1; } if ( y<x ){ return 1; } return 0; } // 判断是否buffer对象 function isBuffer(b){ if ( global.Buffer && typeof global.Buffer.isBuffer==='function' ){ return global.Buffer.isBuffer(b); } return !!(b!=null && b._isBuffer); } var util=require('util/'); var hasOwn=Object.prototype.hasOwnProperty; var pSlice=Array.prototype.slice; // 是否拥有函数名 var functionsHaveNames=(function(){ return function foo(){}.name==='foo'; }()); // 调用toString方法将对象转化为字符串 function pToString(obj){ return Object.prototype.toString.call(obj); } // ArrayBuffer是二进制数据的原始缓冲区,该缓冲区用于存储各种类型化数组的数据 // var buffer=new ArrayBuffer(12); // var x=new Int32Array(buffer);// 也可以直接创建 // // Int8Array 8位二补码有符号整数 // // Uint8Array 8位无符号整数 // // Int16Array 16位二补码有符号整数 // // Uint16Array 16位无符号整数 // // Int32Array 32位二补码有符号整数 // // Uint32Array 32位无符号整数 // // Float32Array 32位 IEEE 浮点数 // // Float64Array 64位 IEEE 浮点数 // x[0]=1234;console.log(x[0]) // 1234 // DataView可以对数据作更细致的操作,即每项设置不同的数据类型 // var buffer=new ArrayBuffer(12); // var x=new DataView(buffer, 0); // x.setInt8(0, 22); // x.setFloat32(1, Math.PI); // console.log(x.getInt8(0)); // 22 // console.log(x.getFloat32(1)); // 3.1415927410125732 // 判断是否ArrayBuffer对象 function isView(arrbuf){ if ( isBuffer(arrbuf) ){ return false; } if ( typeof global.ArrayBuffer!=='function' ){ return false; } if ( typeof ArrayBuffer.isView==='function' ){ return ArrayBuffer.isView(arrbuf); } if ( !arrbuf ){ return false; } if ( arrbuf instanceof DataView ){ return true; } if ( arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer ){ return true; } return false; } var assert=module.exports=ok; // 获取函数名 var regex=/\s*function\s+([^\(\s]*)\s*/; function getName(func){ if ( !util.isFunction(func) ){ return; } if ( functionsHaveNames ){ return func.name; } var str=func.toString(); var match=str.match(regex); return match && match[1]; } // AssertionError形式的错误对象,继承Error错误对象,用于校验失败后抛出 // new assert.AssertionError({ message: message, // actual: actual, // expected: expected }) assert.AssertionError=function AssertionError(options){ this.name='AssertionError'; this.actual=options.actual;// 实际值 this.expected=options.expected;// 期望值 this.operator=options.operator;// 分割符 if ( options.message ){ this.message=options.message; this.generatedMessage=false; }else{ this.message=getMessage(this);// 以实际值+operator+期望值的形式构建默认错误消息 this.generatedMessage=true; } // 获取错误的堆栈信息 var stackStartFunction=options.stackStartFunction || fail; if ( Error.captureStackTrace ){ Error.captureStackTrace(this, stackStartFunction); }else{ // non v8 browsers so we can have a stacktrace var err=new Error(); if ( err.stack ){ var out=err.stack; // try to strip useless frames var fn_name=getName(stackStartFunction); var idx=out.indexOf('\n'+fn_name); if ( idx>=0 ){ // once we have located the function frame // we need to strip out everything before it (and its line) var next_line=out.indexOf('\n', idx+1); out=out.substring(next_line+1); } this.stack = out; } } }; util.inherits(assert.AssertionError, Error); // 截取字符串 function truncate(s, n) { if ( typeof s==='string' ){ return s.length<n ? s : s.slice(0, n); } else { return s; } } // 将对象转化为字符串后输出,或将函数转化为[Function: fnName]的形式 function inspect(something){ if ( functionsHaveNames || !util.isFunction(something) ){ return util.inspect(something);// 将任意对象转化成字符串 } var rawname=getName(something); var name=rawname ? ': '+rawname : ''; return '[Function'+name+']'; } // 以实际值+分割符+期望值的形式构建默认错误消息 function getMessage(self) { return truncate(inspect(self.actual), 128)+' '+ self.operator+' '+ truncate(inspect(self.expected), 128); } // 配置AssertionError的实际值、期望值、错误消息、分割符、堆栈函数,并抛出错误 function fail(actual, expected, message, operator, stackStartFunction){ throw new assert.AssertionError({ message: message, actual: actual, expected: expected, operator: operator, stackStartFunction: stackStartFunction }); } assert.fail=fail; // 校验value是否为真 function ok(value, message){ if (!value) fail(value, true, message, '==', assert.ok); } assert.ok=ok; // 校验实际值和期望值是否相等 assert.equal=function equal(actual, expected, message){ if ( actual!=expected ) fail(actual, expected, message, '==', assert.equal); }; // 校验实际值和期望值是否不相等 assert.notEqual=function notEqual(actual, expected, message){ if ( actual==expected ){ fail(actual, expected, message, '!=', assert.notEqual); } }; // 对象等深度比较,非严格模式值比较 assert.deepEqual=function deepEqual(actual, expected, message){ if ( !_deepEqual(actual, expected, false) ){ fail(actual, expected, message, 'deepEqual', assert.deepEqual); } }; // 对象等深度比较,非严格模式引用地址比较 assert.deepStrictEqual=function deepStrictEqual(actual, expected, message){ if ( !_deepEqual(actual, expected, true) ){ fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); } }; // buffer对象、ArrayBuffer对象、时间对象、正则对象、数组、对象比较 function _deepEqual(actual, expected, strict, memos){ if ( actual===expected ){ return true; // buffer对象相等比较 }else if( isBuffer(actual) && isBuffer(expected) ){ return compare(actual, expected)===0; // 时间对象比较 }else if( util.isDate(actual) && util.isDate(expected) ){ return actual.getTime()===expected.getTime(); // 正则对象比较 }else if( util.isRegExp(actual) && util.isRegExp(expected) ){ return actual.source===expected.source && actual.global===expected.global && actual.multiline===expected.multiline && actual.lastIndex===expected.lastIndex && actual.ignoreCase===expected.ignoreCase; // null值比较,分为严格比较和不严格比较 }else if( (actual===null || typeof actual!=='object') && (expected===null || typeof expected !== 'object')){ return strict ? actual===expected : actual==expected; // 判断ArrayBuffer对象是否相等 }else if( isView(actual) && isView(expected) && pToString(actual)===pToString(expected) && !(actual instanceof Float32Array || actual instanceof Float64Array) ){ return compare(new Uint8Array(actual.buffer),new Uint8Array(expected.buffer))===0; }else if( isBuffer(actual)!==isBuffer(expected) ){ return false; }else{ // 关于memos以及数组相等判断 memos=memos || {actual: [], expected: []}; var actualIndex=memos.actual.indexOf(actual); if ( actualIndex!==-1 ){ if ( actualIndex===memos.expected.indexOf(expected) ){ return true; } } memos.actual.push(actual); memos.expected.push(expected); return objEquiv(actual, expected, strict, memos); } } // 是否arguments参数对象 function isArguments(object){ return Object.prototype.toString.call(object)=='[object Arguments]'; } // arguments参数对象、对象、null、undefined比较 function objEquiv(a, b, strict, actualVisitedObjects){ if ( a===null || a===undefined || b===null || b===undefined ) return false; // util.isPrimitive??? if ( util.isPrimitive(a) || util.isPrimitive(b) ) return a===b; if ( strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b) ) return false; // arguments参数对象比较 var aIsArgs=isArguments(a); var bIsArgs=isArguments(b); if ( (aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs) ) return false; if ( aIsArgs ){ a=pSlice.call(a); b=pSlice.call(b); return _deepEqual(a,b,strict); } // 对象比较,先比较键,再比较值 var ka=objectKeys(a); var kb=objectKeys(b); var key, i; if ( ka.length!==kb.length ) return false; ka.sort(); kb.sort(); for ( i=ka.length-1; i>=0; i-- ){ if ( ka[i]!==kb[i] ) return false; } // 递归比较值 for ( i=ka.length-1; i>=0; i-- ){ key=ka[i]; if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) return false; } return true; } // 对象等深度比较,非严格模式值比较 assert.notDeepEqual=function notDeepEqual(actual, expected, message){ if (_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); } }; // 对象等深度比较,非严格模式引用地址比较 assert.notDeepStrictEqual=notDeepStrictEqual; function notDeepStrictEqual(actual, expected, message){ if (_deepEqual(actual, expected, true)) { fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); } } // ===相等判断 assert.strictEqual = function strictEqual(actual, expected, message) { if (actual !== expected) { fail(actual, expected, message, '===', assert.strictEqual); } }; // !==不相等判断 assert.notStrictEqual = function notStrictEqual(actual, expected, message) { if (actual === expected) { fail(actual, expected, message, '!==', assert.notStrictEqual); } }; // 用正则、是否实例、函数校验由block函数获得的错误actual function expectedException(actual, expected){ if ( !actual || !expected ){ return false; } // 当block函数抛出错误throw new Error("Wrong value"),捕获后用正则/value/校验该error为真 if ( Object.prototype.toString.call(expected)=='[object RegExp]' ){ return expected.test(actual); } try{ if ( actual instanceof expected ){ return true; } }catch (e){ // Ignore. The instanceof check doesn't work for arrow functions. } if ( Error.isPrototypeOf(expected) ){ return false; } return expected.call({}, actual) === true; } // 执行block,有错则捕获错误,返回为真,否则为否 function _tryBlock(block){ var error; try { block(); }catch (e){ error=e; } return error; } // shouldThrow为真时,block没有错误抛出AssertionError错误,或者错误不符期望,抛出block捕获的错误 // shouldThrow为否时,没有期望或者block捕获错误符合期望,抛出AssertionError错误 // block捕获错误不符合期望,抛出block捕获的错误 function _throws(shouldThrow, block, expected, message){ var actual; if (typeof block!=='function'){ throw new TypeError('"block" argument must be a function'); } if ( typeof expected==='string' ){ message=expected; expected=null; } actual=_tryBlock(block);// 获取错误或undefined message=(expected && expected.name ? ' ('+expected.name+').' : '.')+ (message ? ' '+message : '.'); if ( shouldThrow && !actual ){ fail(actual, expected, 'Missing expected exception'+message); } var userProvidedMessage=typeof message==='string'; var isUnwantedException=!shouldThrow && util.isError(actual); var isUnexpectedException=!shouldThrow && actual && !expected; // shouldThrow为否,且actual是期望捕获的错误,用AssertionError的方式抛出错误 if ( (isUnwantedException && userProvidedMessage && expectedException(actual, expected)) || isUnexpectedException ){ fail(actual, expected, 'Got unwanted exception'+message); } // shouldThrow为真,且actual不是期望捕获的错误,或者shouldThrow为否,actual捕获到错误,抛出actual错误 if ( (shouldThrow && actual && expected && !expectedException(actual, expected)) || (!shouldThrow && actual) ){ throw actual; } } // block没有错误抛出AssertionError错误,或者错误不符期望,抛出block捕获的错误 assert.throws=function(block, /*optional*/error, /*optional*/message){ _throws(true, block, error, message); }; // 没有期望或者block捕获错误符合期望,抛出AssertionError错误 // block捕获错误不符合期望,抛出block捕获的错误 assert.doesNotThrow=function(block, /*optional*/error, /*optional*/message) { _throws(false, block, error, message); }; // 参数为Error对象,直接抛出该错误对象 assert.ifError=function(err){if (err) throw err;}; // 获取对象的键 var objectKeys=Object.keys || function (obj){ var keys=[]; for ( var key in obj ){ if ( hasOwn.call(obj, key) ) keys.push(key); } return keys; };