您的位置:首页 > Web前端 > Node.js

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模块导出的都是一些方法(函数),其实主要就是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;}};


所以,从上述的三个部分来分析源码,是不是就很容易理解这个模块了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  nodejs