Nodejs源码分析之assert

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leoleocs/article/details/50342703

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;}};

所以,从上述的三个部分来分析源码,是不是就很容易理解这个模块了。

展开阅读全文

没有更多推荐了,返回首页