【问题标题】:Mocha beforeEach vs before executionMocha beforeEach vs 执行前
【发布时间】:2015-12-16 02:07:17
【问题描述】:

我最近遇到了一个我无法解释的问题。我在这些测试中有很多代码,所以我将尽我所能在这里捕捉到这个想法

我的测试看起来像:

describe('main page', function() {
  beforeEach(function(done){
    addUserToMongoDb(done);   // #1
  });

  afterEach(function(done) {
    removeUserFromMongoDb(done);
  });

  context('login', function() {
     it('should log the user in', function() {
       logUserIn(user_email);  // #2 - This line requires the user from the beforeEach
     });
  });

  context('preferences', function() {
    before(function(done) {    //#3
       logUserInBeforeTest(user_email);
     });

    it('should show the preferences', function() {
       doCheckPreferences(); // #4
    });
  });
});

问题是,#1 的 beforeEach 运行良好。我可以看到它发生在数据库上,#2 中的测试通过了。

但是,#4 的首选项上下文中的测试失败,因为它无法找到在#3 登录的用户。

似乎上下文before 在描述beforeEach 之前执行,这导致它们失败。如果我将logUserIn 移动到it 块中,它可以正常工作。

这是什么原因造成的?

【问题讨论】:

  • 无法解释为什么“似乎之前的上下文是在 describe beforeEach 之前执行的”,但你不应该在某个时候执行done以前?
  • Before 是在整个块之前,beforeEach 是在每个测试之前。
  • @StevenScott 所以这就是为什么。 describe beforeEach 在之前的上下文之后运行。我希望文档让这一点更明显

标签: node.js mocha.js


【解决方案1】:

虽然此答案只是再次显示文档说明,并且有一些 cmets 试图帮助显示差异,但应参考以下@tomasz-wszelaki 的答案。

Mocha 的测试运行器在Hooks section of the Mocha Test Runner 中对这个功能的解释最好。

来自 Hooks 部分:

describe('hooks', function() {

    before(function() {
        // runs before all tests in this file regardless where this line is defined.
    });

    after(function() {
        // runs after all tests in this file
    });

    beforeEach(function() {
        // runs before each test in this block
    });

    afterEach(function() {
        // runs after each test in this block
    });

    // test cases
});

您可以将这些例程嵌套在其他也可以具有 before/beforeEach 例程的描述块中。

【讨论】:

  • 恕我直言,文档并没有很好地解释这种行为,正如关于这个特定主题的大量问题所证明的那样。 @tomasz-wszelaki 的回答比逐字引用文档更有启发性。
  • @Aea 感谢您的评论。我阅读了另一个答案,并且这个例子看起来更清楚,所以我已经更新了我的 cmets 关于这个问题。在复制样本时,我没有考虑 Tomasz 所做的结构。
【解决方案2】:

我发现了一个类似的问题。该文档具有误导性,因为“在此块之前”意味着(至少对我而言)“在此描述部分之前”。同时它的意思是“在任何描述部分之前”。检查这个例子:

describe('outer describe', function () {
    beforeEach(function () {
        console.log('outer describe - beforeEach');
    });

    describe('inner describe 1', function () {
        before(function () {
            console.log('inner describe 1 - before');
        });

    describe('inner describe 2', function () {
        beforeEach(function () {
            console.log('inner describe 2 - beforeEach');
        });
 });

 // output will be:
 // inner describe 1 - before
 // outer describe - beforeEach
 // inner describe 2 - beforeEach

您将before 放在层次结构中的哪个位置似乎无关紧要 - 它会在任何描述之前运行,而不是在其包含的描述之前运行。

【讨论】:

  • 把它想象成“在每个块之前”。这意味着在每次测试之前和每次描述之前。
  • 感谢您的澄清!帽子是我问自己的问题,因为我在我们的项目中看到 before() 钩子在嵌套的 describe() 中定义,所以人们认为它是在描述上下文的关系中运行的,但事实并非如此。 Mocha 文档在这件事上可能更精确,充其量,除了最上面的 describe() 之外,其他任何地方都不应该允许它。
  • 感谢您的解释。我很震惊地发现 Mocha 发现这个实现之前更多的是一个功能而不是一个错误。为什么它不尊重块(描述/上下文)范围?!
【解决方案3】:

混淆的原因在于 mocha 的文档。 你可以在mocha找到:

测试可以出现在你的钩子之前、之后或穿插在你的钩子中。 Hooks 将按照它们定义的顺序运行,视情况而定;所有 before() 钩子运行(一次),然后是任何 beforeEach() 钩子、测试、任何 afterEach() 钩子,最后是 after() 钩子(一次)。

讨论的钩子 beforebeforeEach 分别在所有或每个 it 之前执行 - 在 describe 部分之前无法执行它。

在这里,您可以 find 对 mocha 主分支的 #1 贡献者的回答添加类似 beforeDescribe 钩子的内容。

我认为你应该看看--delay mocha option

【讨论】:

    【解决方案4】:

    关键是要有mocha.opts 文件,其中行指向./test/bootstrap.js,您可以在其中应用 before、beforeAll、after、afterAll 挂钩。

    Execute all tests:
     - npm test
    
    Execute a single test:
    - NODE_ENV=test node --inspect ./node_modules/.bin/_mocha --opts test/mocha.opts test/test/service/unSubscriber.test.js 
    

    node --inspect 调试标志


    /package.json

    {
      "name": "app",
      "version": "0.0.1",
      "engines": {
        "node": "11.9.0",
        "npm": "6.5.0"
      },
      "scripts": {
        "test": "NODE_ENV=test node --inspect ./node_modules/.bin/_mocha --opts test/mocha.opts test/**/**/**/*.js"
      },
      "private": true,
      "dependencies": {
        "express": "3.21.2",
        "mongoose": "^4.5.10",
        ...
      },
      "devDependencies": {
        "chai": "^4.2.0",
        "faker": "^4.1.0",
        "mocha": "^6.0.0"
      }
    }
    
    

    /test/mocha.opts

    --recursive
    --timeout 30000
    --reporter spec
    --file ./test/bootstrap.js
    

    /test/bootstrap.js

    const mongoose = require('mongoose');
    const config = require('./../service/config').getConfig();
    mongoose.Promise = global.Promise;
    
    before((done) => {
      (async () => {
        const connection = await mongoose.connect(config.mongo_url, { useMongoClient: true });
        await connection.db.dropDatabase();
      })().then(() => {
        require('../server');
        done();
      });
    });
    
    after((done) => {
      process.kill(process.pid, 'SIGTERM');
      done();
    });
    
    

    /server.js

    const http = require('http');
    const app = require('./app');
    const config = require('./service/config');
    const port = process.env.PORT || 4000;
    
    const server = http.createServer(app);
    
    server.listen(port, () => {
      console.log(`===== Server running:${config.getEnv()}=====`);
    });
    
    process.on('SIGTERM', () => {
      console.log('===== Server closed =====');
      process.exit(0);
    });
    
    

    /test/service/unSubscriber.test.js

    const faker = require('faker');
    
    const ContactOptOutRepository = require('../../repository/contactOptOut');
    const UnSubscriber = require('../../service/unSubscriber');
    const expect = require('chai').expect;
    
    const contactOptOutRepository = new ContactOptOutRepository();
    const unSubscriber = new UnSubscriber();
    
    const emails = [
      faker.internet.email(),
      faker.internet.email(),
      faker.internet.email(),
      faker.internet.email(),
      faker.internet.email(),
    ];
    
    describe('UnSubscriber', () => {
      it('should filter out unsubscribed emails', () => {
        return (async () => {
          await contactOptOutRepository.newUnSubscription(emails[0], faker.lorem.word());
          await contactOptOutRepository.newUnSubscription(emails[1], faker.lorem.word());
          await contactOptOutRepository.newUnSubscription(emails[2], faker.lorem.word());
    
          return await unSubscriber.filterUnsubscribed(emails);
        })()
          .then(filtered => {
            expect(filtered.length).to.be.equal(2);
          });
      });
    });
    
    
    

    【讨论】:

      猜你喜欢
      • 2019-10-14
      • 2019-03-04
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 2017-02-05
      相关资源
      最近更新 更多