Nodejs源码分析之assert
2015-12-17 18:46
555 查看
assert库是nodejs中的一个工具库, 主要用于测试模块,这个在自动化测试框架中用的最多。 具体的用法可以参见官方文档:
https://nodejs.org/api/assert.html
大致的用法可以分为下面几个部分:
测试是否相等,这类接口与方法最多,以assert, assert.ok, assert.deepEqual, assert.deepStrictEqual, assert.equal,assert.strictEqual等,主要从是相当/不相等, ==/===角度去比较
测试是否抛出异常: assert.throws和assert.doesNotThrow,测试一个代码段是否会有异常,assert.ifError为直接测试值是否为真而抛出异常
帮助函数: assert.fail,,assert.AssertionError
这里就分析具体的源码 assert.js。
所以 接口通常以assert来表示, 而帮助函数主要是fail和AssertionError ,前者是抛出一个异常,而后者主要是AssertionError ,继承了系统级Error的函数 (这个以后深挖)。
所以,从上述的三个部分来分析源码,是不是就很容易理解这个模块了。
https://nodejs.org/api/assert.html
大致的用法可以分为下面几个部分:
测试是否相等,这类接口与方法最多,以assert, assert.ok, assert.deepEqual, assert.deepStrictEqual, assert.equal,assert.strictEqual等,主要从是相当/不相等, ==/===角度去比较
测试是否抛出异常: assert.throws和assert.doesNotThrow,测试一个代码段是否会有异常,assert.ifError为直接测试值是否为真而抛出异常
帮助函数: assert.fail,,assert.AssertionError
这里就分析具体的源码 assert.js。
接口导出与帮助函数
assert模块导出的都是一些方法(函数),其实主要就是assert,请看下面的源代码// 1. The assert module provides functions that throw // AssertionError's when particular conditions are not met. The // assert module must conform to the following interface. // 也就是说assert模块提供抛出AssertionError当某些条件不成立的时候 // 其导出的接口为assert变量。 var assert = module.exports = ok; // 2. The AssertionError is defined in assert. // new assert.AssertionError({ message: message, // actual: actual, // expected: expected }) // 该函数就是该模块测试后,显示异常的函数,该函数是该模块最后显示异常的帮助函数 // options含有如下属性: // @actual, 实际值 // @expected,期望值 // @operator,操作符,一般是 ==, === 或者其他表示比较的文字 // @message, 需要显示的消息 assert.AssertionError = function AssertionError(options) { this.name = 'AssertionError'; this.actual = options.actual; this.expected = options.expected; this.operator = options.operator; // 当有消息时, 其实actual和expected 等是没有被用到的 if (options.message) { this.message = options.message; this.generatedMessage = false; } else { this.message = getMessage(this); this.generatedMessage = true; } var stackStartFunction = options.stackStartFunction || fail; // 获取当前的堆栈信息,stackStartFunction是开始函数(从开始函数的堆栈信息) Error.captureStackTrace(this, stackStartFunction); }; // assert.AssertionError instanceof Error //这里表示assert.AssertionError函数继承了Error里的属性 // Error应该是平台定义的一个函数,可以获取当前的堆栈信息。 util.inherits(assert.AssertionError, Error); // 获取当前需要显示的消息,可以看出,主要是由 实际的, 期望的与操作符的值进行比较。 function getMessage(self) { return truncate(util.inspect(self.actual, {depth: null}), 128) + ' ' + self.operator + ' ' + truncate(util.inspect(self.expected, {depth: null}), 128); } // 3. All of the following functions must throw an AssertionError // when a corresponding condition is not met, with a message that // may be undefined if not provided. All assertion methods provide // both the actual and expected values to the assertion error for // display purposes. // // 这个是对AssertionError的进一步封装, 直接抛出AssertionError异常, // 也就是说该函数的目的就是抛出异常,这个是帮助函数,当不满足特定条件(相等/不相等,异常/无异常)时。 // 注意: 如果有异常消息,直接显示异常消息,如果没有,直接显示实际值,期望值和操作符 //组成的字符串,具体实现在getMessage function fail(actual, expected, message, operator, stackStartFunction) { throw new assert.AssertionError({ message: message, actual: actual, expected: expected, operator: operator, stackStartFunction: stackStartFunction }); }
所以 接口通常以assert来表示, 而帮助函数主要是fail和AssertionError ,前者是抛出一个异常,而后者主要是AssertionError ,继承了系统级Error的函数 (这个以后深挖)。
测试相等/不相等函数
分析源码如下:// 4. Pure assertion tests whether a value is truthy, as determined // by !!guard. // assert.ok(guard, message_opt); // This statement is equivalent to assert.equal(true, !!guard, // message_opt);. To test strictly for the value true, use // assert.strictEqual(true, guard, message_opt);. // 直接测试value是否为真,如果不为真,直接抛出异常 function ok(value, message) { if (!value) fail(value, true, message, '==', assert.ok); } // 导出了ok方法 assert.ok = ok; // 5. The equality assertion tests shallow, coercive equality with // ==. // assert.equal(actual, expected, message_opt); // 直接测试是否相等 == 如果不相等,直接抛出异常 // 注意,这里是 == 不是 === assert.equal = function equal(actual, expected, message) { if (actual != expected) fail(actual, expected, message, '==', assert.equal); }; // 6. The non-equality assertion tests for whether two objects are not equal // with != assert.notEqual(actual, expected, message_opt); // 直接测试不相等,如果相等 == ,抛出异常 assert.notEqual = function notEqual(actual, expected, message) { if (actual == expected) { fail(actual, expected, message, '!=', assert.notEqual); } }; // 7. The equivalence assertion tests a deep equality relation. // assert.deepEqual(actual, expected, message_opt); // 深度相等,注意和上面的equal以及strictEqual相等(===)区分开 // 如果不是深度相等,直接抛出异常 assert.deepEqual = function deepEqual(actual, expected, message) { if (!_deepEqual(actual, expected)) { fail(actual, expected, message, 'deepEqual', assert.deepEqual); } }; // 深度相等的比较函数,请看下面的帮助函数: // 理解深度比较函数 function _deepEqual(actual, expected) { // 7.1. All identical values are equivalent, as determined by ===. // step1: 如果严格相等,直接返回真 if (actual === expected) { return true; } // step2:如果对象都是Buffer,并且buffer中的值相等,就相等,否则不等 else if (b.Buffer.isBuffer(actual) && b.Buffer.isBuffer(expected)) { if (actual.length != expected.length) return false; for (var i = 0; i < actual.length; i++) { if (actual[i] !== expected[i]) return false; } return true; // 7.2. If the expected value is a Date object, the actual value is // equivalent if it is also a Date object that refers to the same time. // step3: 如果对象都是日期,获取的日期严格相等,就深度相等 } else if (util.isDate(actual) && util.isDate(expected)) { return actual.getTime() === expected.getTime(); // 7.3 If the expected value is a RegExp object, the actual value is // equivalent if it is also a RegExp object with the same source and // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). // step4: 如果对象都是正则表达式的情况,表达式的所有属性相等,就深度相等 } 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; // 7.4. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. // step5:如果都不是对象类型,直接 == 就表示是否相等了,这里比较的都是基础类型 } else if (!util.isObject(actual) && !util.isObject(expected)) { return actual == expected; // 7.5 For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified // with Object.prototype.hasOwnProperty.call), the same set of keys // (although not necessarily the same order), equivalent values for every // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else { // step6:直接比较对象的所有属性 return objEquiv(actual, expected); } } // 8. The non-equivalence assertion tests for any deep inequality. // assert.notDeepEqual(actual, expected, message_opt); // 期待不深度相等,如果深度相等,抛出异常 assert.notDeepEqual = function notDeepEqual(actual, expected, message) { if (_deepEqual(actual, expected)) { fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); } }; // 9. The strict equality assertion tests strict equality, as determined by ===. // assert.strictEqual(actual, expected, message_opt); // 这个是严格相等, ===, 如果不严格相等,抛出异常 assert.strictEqual = function strictEqual(actual, expected, message) { if (actual !== expected) { fail(actual, expected, message, '===', assert.strictEqual); } }; // 10. The strict non-equality assertion tests for strict inequality, as // determined by !==. assert.notStrictEqual(actual, expected, message_opt); // 期待不严格相等,如果严格相等,直接抛出异常 assert.notStrictEqual = function notStrictEqual(actual, expected, message) { if (actual === expected) { fail(actual, expected, message, '!==', assert.notStrictEqual); } };
测试是否抛出异常
这里主要有两个函数assert.throws 和assert.doesNotThrow,测试一段代码是否抛出异常和没有抛出异常。不多说,直接看源代码:// 期待异常是否是期待的,如果是,返回真,否则,返回假 function expectedException(actual, expected) { / 如果actual 或者expected为空(null或者undefined),直接返回false if (!actual || !expected) { return false; } if (Object.prototype.toString.call(expected) == '[object RegExp]') { return expected.test(actual); } else if (actual instanceof expected) { return true; } else if (expected.call({}, actual) === true) { return true; } return false; } // 抛出异常的帮助函数 //@shouldThrow 布尔值,表示期待抛出异常 //@block 需要测试的代码段 //@expected 期待的错误,如果shouldThrow 为真的情况下 //@message 异常的消息(当测试失败时) function _throws(shouldThrow, block, expected, message) { var actual; // 如果expected是一个字符串,需要重新赋值expected和message。 // 这样处理,主要是因为doesNotThrow和throws的函数的参数区别 if (util.isString(expected)) { message = expected; expected = null; } // 测试是否有异常 try { block(); } catch (e) { // 如果有具体的异常 actual = e; } // 生成失败是需要显示的消息 message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + (message ? ' ' + message : '.'); // 如果期待有异常,但是实际没异常的情况, 抛出异常 if (shouldThrow && !actual) { fail(actual, expected, 'Missing expected exception' + message); } // 期待没有异常,但是有异常,抛出异常 if (!shouldThrow && expectedException(actual, expected)) { fail(actual, expected, 'Got unwanted exception' + message); } // 如果期待异常,实际也是有异常,但是异常不相等,就抛出实际的异常 if ((shouldThrow && actual && expected && !expectedException(actual, expected)) || (!shouldThrow && actual)) { throw actual; } } // 11. Expected to throw an error: // assert.throws(block, Error_opt, message_opt); // 期待抛出异常,注意实际使用的时候:这个error一定会传入参数,否则,会始终抛出异常而通不过测试 assert.throws = function(block, /*optional*/error, /*optional*/message) { // 可以看出,这个直接调用的上面的_throws函数 _throws.apply(this, [true].concat(pSlice.call(arguments))); }; // 测试期待没有异常 assert.doesNotThrow = function(block, /*optional*/message) { // 可以看出,这个直接调用的上面的_throws函数 _throws.apply(this, [false].concat(pSlice.call(arguments))); }; //测试参数error是否为真,如果为真,直接抛出异常 assert.ifError = function(err) { if (err) {throw err;}};
所以,从上述的三个部分来分析源码,是不是就很容易理解这个模块了。
相关文章推荐
- 使用ruby部署工具mina快速部署nodejs应用教程
- Google官方支持的NodeJS访问API,提供后台登录授权
- 浅谈Nodejs观察者模式
- nodejs教程之环境安装及运行
- nodejs中的fiber(纤程)库详解
- 基于NodeJS的前后端分离的思考与实践(五)多终端适配
- 基于NodeJS的前后端分离的思考与实践(二)模版探索
- 我的NodeJs学习小结(一)
- nodejs中实现sleep功能实例
- Nodejs异步回调的优雅处理方法
- Windows系统下使用Sublime搭建nodejs环境
- nodejs实现获取某宝商品分类
- nodejs简单实现中英文翻译
- Node.js插件的正确编写方式
- 使用upstart把nodejs应用封装为系统服务实例
- NodeJS Web应用监听sock文件实例
- Nodejs学习笔记之测试驱动
- Nodejs学习笔记之Stream模块
- 如何正确使用Nodejs 的 c++ module 链接到 OpenSSL
- Nodejs为什么选择javascript为载体语言