摩卡的 before() 函数的目的是什么?



Mocha 有几个"钩子",用于在独立于测试用例本身的测试中运行辅助功能(清除数据库、创建模拟文件等(。

但是,在before()的情况下(不是beforeEach(),我得到的那个(,这似乎是多余的。

before()在当前套件中的所有测试之前运行一次逻辑,那么为什么我甚至需要将其包装在函数中呢?

以下两者之间没有明显的区别:

describe('Something', function() {
    before(doSomePreTestLogic);
    //Tests ahoy
});

describe('Something', function() {
    doSomePreTestLogic();
    //Tests ahoy
});

before()包装我的预测试逻辑有什么意义?

测试中选择before()钩子有几个不同的原因:

语义学

这可能是最大的一个。 有时,您可能需要执行任务,例如在数据库中填充一些虚拟数据,然后再运行实际测试。 您当然可以在测试本身中执行此操作,但是如果您在预填充时以及在运行实际测试用例之前遇到错误,则会中断报告。 通过有一个before()钩子,你有一个逻辑上、语义上有效的位置来插入这种逻辑。

异步测试的清理器

就像摩卡测试可以是异步的一样,你的before()钩子逻辑也可以是异步的。 回到预填充示例,这很方便,因为这意味着所有异步预测试逻辑都不会强制所有实际测试逻辑进行另一级别的缩进。

与其他钩子的一致性:

摩卡还提供了其他几个钩子,即:after()beforeEach()afterEach()。 您可能也可以对所有这些其他钩子提出相同的问题,但是如果您认为它们中的任何一个都具有经过验证的存在,那么确实需要包含before()以完善API。

结果

直接放入将生成测试套件的describe回调代码中。我说的是调用it,但也包括可以循环表或文件以声明一堆测试(通过在循环中调用it(的函数。

将实际初始化测试所依赖的状态的代码放在钩子内。

所有其他考虑都是次要的。

让我解释一下...

背景

Mocha 分两个阶段执行测试套件:

  1. 它发现存在哪些测试。在此阶段,它将立即执行传递给describe的回调,并记录传递给声明测试的函数(it等(和声明钩子的函数(beforebeforeEachafter等(的回调以供将来调用

  2. 它运行测试。在此阶段,它将运行之前记录的回调。

区别

因此,请考虑以下示例:

function dump () { console.log("running:", this.test.fullTitle()); }
describe("top", function () {
    before(dump);
    it("test 1", dump);
    it("test 2", dump);
    describe("level 1", function () {
        before(dump);
        it("test 1", dump);
        it("test 2", dump);
    });
});

请注意,fullTitle给出了测试的全名,从顶级describe开始,通过任何嵌套describe,直到包含调用的it或钩子。使用spec报告器运行并仅保留running:行,您将获得:

running: top "before all" hook: dump
running: top test 1
running: top test 2
running: top level 1 "before all" hook: dump
running: top level 1 test 1
running: top level 1 test 2
请注意钩子的

顺序,以及每个钩子在其各自的describe回调中声明的测试之前如何执行。

那么考虑一下这个套件:

function dump () { console.log("running:", this.test.fullTitle()); }
function directDump() { console.log("running (direct):", this.fullTitle()); }
describe("top", function () {
    directDump.call(this);
    it("test 1", dump);
    it("test 2", dump);
    describe("level 1", function () {
        directDump.call(this);
        it("test 1", dump);
        it("test 2", dump);
    });
});

使用spec报告器运行并仅保留running:行,您将获得:

running (direct): top
running (direct): top level 1
running: top test 1
running: top test 2
running: top level 1 test 1
running: top level 1 test 2

请注意对directDump的两个调用是如何先于其他任何调用运行的。

后果

  1. 如果直接放入回调中的任何初始化代码失败describe,则整个运行将立即失败。不会执行任何测试。故事结束。

  2. 如果放入 before 挂钩中的任何初始化代码失败,则包含后果。首先,由于在需要before钩子时运行,因此之前计划的任何测试都有机会运行。此外,摩卡只会跳过那些依赖于before钩的测试。例如,让我们假设这个套件:

    function dump () { console.log("running:", this.test.fullTitle()); }
    describe("top", function () {
        before(dump);
        it("test 1", dump);
        it("test 2", dump);
        describe("level 1", function () {
            before(function () { throw new Error("foo"); });
            it("test 1", dump);
            it("test 2", dump);
        });
        describe("level 1 (second)", function () {
            before(dump);
            it("test 1", dump);
            it("test 2", dump);
        });
    });
    

    如果使用spec报告器运行它,则整个输出(减去堆栈跟踪(将如下所示:

      top
    running: top "before all" hook: dump
    running: top test 1
        ✓ test 1
    running: top test 2
        ✓ test 2
        level 1
          1) "before all" hook
        level 1 (second)
    running: top level 1 (second) "before all" hook: dump
    running: top level 1 (second) test 1
          ✓ test 1
    running: top level 1 (second) test 2
          ✓ test 2
    
      4 passing (5ms)
      1 failing
      1) top level 1 "before all" hook:
         Error: foo
         [stack trace]
    

    请注意 a( 一些测试在失败的钩子之前运行,b( 摩卡仍然运行不依赖于钩子的测试。

语义

,在机器和人类层面。

此外,它还使测试代码与"导出"接口保持一致,例如,

module.exports = {
  before: function(){
    // ...
  },
  'Array': {
    '#indexOf()': {
      'should return -1 when not present': function(){
        [1,2,3].indexOf(4).should.equal(-1);
      }
    }
  }
};

最新更新