Подтвердить что ты не робот

Mocha beforeEach против перед исполнением

Недавно я столкнулся с проблемой, которую я не могу объяснить. У меня есть много кода в этих тестах, поэтому я сделаю все возможное, чтобы захватить идею здесь.

У меня есть тесты, которые выглядят так:

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

Проблема заключается в том, что beforeEach by #1 работает нормально. Я вижу, что это происходит в БД и тесты в #2 pass.

Однако тесты в контексте предпочтений в #4 терпят неудачу, потому что он не может найти пользователя для входа в систему #3.

Кажется, что контекст before выполняется до описания beforeEach, что приводит к сбою. Если я перемещаю logUserIn в блок it, он отлично работает.

Что может быть причиной этого?

4b9b3361

Ответ 1

Бегущий по тестам Mocha объясняет эту функциональность лучше всего в разделе Хукс-тестов по Mocha Test Runner.

Из раздела Крючки:

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.

Ответ 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 - он будет работать до того, как будет описано, а не до его описания.

Ответ 3

Причина путаницы заключается в документации мокко. Вы можете найти в мокко:

Тесты могут появляться до, после или с вкраплениями ваших хуков. Крючки будут работать в том порядке, в котором они определены; все ловушки before() выполняются (один раз), затем любые ловушки beforeEach(), тесты, любые ловушки afterEach() и, наконец, ловушки after() (один раз).

Обсуждаемые перехватчики before и beforeEach выполняются непосредственно перед всем или каждым из it соответственно - нет способа выполнить это до раздела описания.

Здесь вы можете найти ответ автора №1 в ветке master beforeDescribe на идею добавить что-то вроде beforeDescribe hook.

Я думаю, что вы должны взглянуть на вариант мокко --delay.

Ответ 4

Главное, чтобы у вас был файл mocha.opts со строкой, указывающей на ./test/bootstrap.js, где вы применяете до, до всех, после, после всех перехватчиков.

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