【问题标题】:How does the chai expect function work?chai 期望函数是如何工作的?
【发布时间】:2015-07-24 08:12:52
【问题描述】:

从 chai 的 api 中你得到了这样的代码:

.exist

Asserts that the target is neither null nor undefined.

var foo = 'hi'
  , bar = null
  , baz;

expect(foo).to.exist;
expect(bar).to.not.exist;
expect(baz).to.not.exist;

现有部分如何工作? expect 函数返回一个对象,然后对“to”对象进行简单的属性查找。这只是一个属性评估,不是吗?对我来说唯一有意义的是如果存在属性是一个 getter 方法。

怎么了?

【问题讨论】:

  • 这是一个 getter 属性
  • 是的,不太习惯使用它们。
  • 好吧,chai 拥抱他们。你还没看过should
  • 我们甚至在讨论这个问题的事实意味着 chai 的设计具有太多的魔力。

标签: javascript mocha.js chai


【解决方案1】:

chai 公开了一个use 方法来访问chai 导出,它是utils

creating plugins时第三方可以使用此方法,但内部也可以使用它来加载它的界面。

这个方法的实现很简单:

exports.use = function (fn) {
  if (!~used.indexOf(fn)) {
    fn(this, util);
    used.push(fn);
  }

  return this;
};

在内部,它使用它来加载(除其他外)主要的Assertion prototype 和核心断言功能:

var assertion = require('./chai/assertion'); // primary Assertion prototype
exports.use(assertion); // load it

var core = require('./chai/core/assertions'); // core assertion functionality
exports.use(core); // load it

Assertion prototype 公开的方法之一是 addProperty 方法,它允许您向所述prototype 添加属性。

在内部chai 使用此方法将核心断言功能添加到Assertion prototype。例如,所有语言链和断言助手(existempty 等)都是通过这种方式添加的。

语言链:

[ 'to', 'be', 'been'
  , 'is', 'and', 'has', 'have'
  , 'with', 'that', 'which', 'at'
  , 'of', 'same' ].forEach(function (chain) {
    Assertion.addProperty(chain, function () {
      return this;
    });
  });

当特定接口在内部加载时,所有这些功能都可用,例如expect。加载此接口后,每当执行expect 时,都会实例化一个新的Assertion prototype,其中将包含所有功能:

// load expect interface
var expect = require('./chai/interface/expect'); // expect interface
exports.use(expect); // load it

// expect interface
module.exports = function (chai, util) {
  chai.expect = function (val, message) {
    return new chai.Assertion(val, message); // return new Assertion Object with all functionality
  };
};

如您所见,expect 方法接受 val 参数(和可选的 message 参数)。当这个方法被调用(例如expect(foo))时,一个新的Assertion prototype将被实例化并返回,暴露所有的核心功能(允许你做expect(foo).to.exist)。

Assertion Constructor 使用flag util 在映射到传入的val 参数的对象上设置标志值。

  function Assertion (obj, msg, stack) {
    flag(this, 'ssfi', stack || arguments.callee);
    flag(this, 'object', obj); // the 'object' flag maps to the passed in val
    flag(this, 'message', msg);
  }

然后所有exist 都是通过flag util 获取该值,并使用Assertion prototype 上定义的assert 方法评估它是否不等于null

  Assertion.addProperty('exist', function () {
    this.assert(
        null != flag(this, 'object')
      , 'expected #{this} to exist'
      , 'expected #{this} to not exist'
    );
  });

【讨论】:

  • 但是如果测试失败,它如何停止执行期望调用之后的剩余代码?比如it后面有done()回调,就不会被执行……
【解决方案2】:

当您调用expect(foo) 时,会实例化一个新的 Assertion 对象。

to、have、with 和类似的属性只返回那个 Assertion 实例。它们只是为了便于阅读。

但是,在您的示例中,exists 实际上是运行断言的东西。

它是一个属性。他们将属性添加到 Assertion 的方式是它们被定义为 getter 函数 see here.

expect(foo).to.exist 可以分解为:

const assertion = new Assertion;
assertion.exists;

assertion.exists 使用 getter 添加到断言对象中。 这意味着当您执行 assertion.exists 以评估 assertion.exists 的值时,会执行之前提供给 addProperty 的函数。

你可以阅读更多关于getter functions的信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    相关资源
    最近更新 更多