Нет документации о том, _когда_ запускается beforeEach
или afterEach
. Моя интуиция подсказывает, что он должен запускаться before
/ after
каждые describe
/ it
блок в текущем контексте завершается.
Однако поведение, которое я замечаю, заключается в том, что beforeEach
и afterEach
запускаются before
/ after
_every_ it
block в текущем контексте. и все вложенные контексты.
Я создал доказательство концепции, чтобы продемонстрировать, что происходит заметное поведение: https://gist.github.com/twolfson/5883057#file -test-js
Для удобства скопирую / вставлю скрипт и выведу сюда:
describe('An afterEach hook', function () {
afterEach(function () {
console.log('afterEach run!');
});
before(function () {
console.log('before run!');
});
describe('in some nested contexts', function () {
before(function () {
console.log('nested before run!');
});
it('runs after this block', function () {
console.log('nested it run!');
});
it('runs again after this block', function () {
console.log('second nested it run!');
});
});
});
before run!
nested before run!
nested it run!
afterEach run!
second nested it run!
afterEach run!
Я интуитивно предвижу, что afterEach
запускается один раз после second nested it
.
before run!
nested before run!
nested it run!
second nested it run!
afterEach run!
Мой вариант использования интуитивной функциональности - очистка контекста this
после завершения контекста. Текущая реализация будет очищать контекст после каждого утверждения.
afterEach(function () {
var key;
for (key in this) {
delete this[key];
}
});
describe('A banana', function () {
before(function () {
this.banana = new Banana();
});
describe('when peeled', function () {
before(function () {
this.peeledBanana = this.banana.peel();
});
it('is white', function () {
assert.strictEqual(this.peeledBanana.color, 'white');
// `afterEach` is invoked here, cleaning out `this`
});
it('is soft', function () {
// `this.peeledBanana` is no longer defined since `afterEach` cleaned it out
assert.strictEqual(this.peeledBanana.hardness, 'soft');
});
});
});
Примером, сравнимым с этим, может быть открытие транзакции базы данных в каждом новом контексте набора тестов.
Я могу обойти это, вызывая after
в каждом вложенном контексте, но мне кажется, что во фреймворке может быть ловушка.
Для тех, кому интересно, это обходной путь для моего варианта использования:
// Before anything else is run
before(function () {
// Iterate over all of the test suites/contexts
this.test.parent.suites.forEach(function bindCleanup (suite) {
// Attach an afterAll listener that performs the cleanup
suite.afterAll(function cleanupContext () {
var key;
for (key in this) {
delete this[key];
}
});
});
});
+1
afterEach
запускается после каждого экземпляра Runnable
; в вашем случае блок it()
. Если вам не нужно вложенное поведение, не вкладывайте тесты. Изменение этого поведения не является предметом обсуждения.
Если ваш второй тест зависит от чего-то определенного в вашем первом тесте, ваши тесты грязные, и вы делаете это неправильно. Порядок, в котором выполняются ваши тесты, не имеет значения. Напишите свои тесты соответственно, и они будут более ценными.
Кроме того, нет необходимости помещать что-либо на this
90% времени.
describe('A banana', function () {
var banana;
before(function () {
banana = new Banana();
});
describe('when peeled', function () {
var peeledBanana;
beforeEach(function () {
// I'm assuming peel() has no side-effects since it returns a new object.
peeledBanana = banana.peel();
});
it('is white', function () {
assert.strictEqual(peeledBanana.color, 'white');
});
it('is soft', function () {
assert.strictEqual(peeledBanana.hardness, 'soft');
});
});
});
Есть ли способ запускать методы перехвата на уровне описания () вместо уровня it ()? Это может быть полезно в некоторых сценариях.
@RathaKM Какой
Не уверен, что это хороший пример, но будет ли следующий хороший вариант использования для этого? @RathaKM не стесняйтесь исправлять.
describe('A banana', function () {
var banana;
beforeEach(function () {
banana = new Banana();
});
describe('when peeled', function () {
var peeledBanana;
before(function () {
// Lets assume peel() HAS side-effects and doesn't return a new object.
banana.peel();
peeledBanana = banana;
});
it('is white', function () {
assert.strictEqual(peeledBanana.color, 'white');
});
it('is soft', function () {
assert.strictEqual(peeledBanana.hardness, 'soft');
});
});
describe('when set on FIRE', function () {
var flamingBanana;
before(function () {
// Same assumption as above
banana.setOnFire();
flamingBanana = banana
});
it('is hot', function () {
assert.isAbove(flamingBanana.temperature, 9000);
});
});
});
@cpoonolly : спасибо за ваши комментарии и верните его. :) Ваш пример - это то, что логически должно работать на уровне описания (BeforeEach). Но этого можно добиться с помощью 'before ()' в каждом описании.
Проблема, с которой я столкнулся, приведена в приведенном ниже примере.
var testData = [{country: 'NIL'}],
dataDriven = require('data-driven'),
assert = require('assert');
describe('@Testing_For_Describe_Level_Hook_Method_To_Update_TestData@', function () {
describe('<strong i="8">@Updated</strong> testData for US@', function () {
before(function (done) {
testData = [{country: 'US'}];
done();
});
dataDriven(testData, function () {
it('expecting updated testData for US', function (ctx, done) {
assert.equal(ctx.country, 'US');
done();
});
});
});
describe('<strong i="9">@Updated</strong> testData for UK@', function () {
before(function (done) {
testData = [{country: 'UK'}];
done();
});
dataDriven(testData, function () {
it('expecting updated testData for UK', function (ctx, done) {
assert.equal(ctx.country, 'UK');
done();
});
});
});
});
Здесь набор тестов не сработает. Мы обновляем _testData_ внутри метода перехвата 'before ()', который будет вызываться непосредственно перед блоком '_it_'. Но мы используем переменную _testData_ в _dataDriven () _, которая будет выполняться перед выполнением блока _it_.
Итак, что нам нужно, это место для обновления переменной до начала выполнения блока _'it'_. Может быть, нам нужно выполнение метода ловушки _before () _ непосредственно перед блоком «описать» (метод ловушки уровня описания).
Надеюсь, теперь проблема ясна.
Было ли это дополнительное обсуждение?
почему нет возможности получить что-то подобное?
describe('Create Article', () => {
beforeEach(() => console.log('CLEAN DB'))
context('When Article is valid', () => {
before(() => console.log("INSERTING VALID ARTICLE"))
it('should not fail')
it('should have some properties')
it('should send an email')
})
context('When Article is not valid', () => {
before(() => console.log("INSERTING NOT VALID ARTICLE"))
it('should fail')
it('should not send an email')
})
})
context
.it
Разве это не законный вариант использования?
@ marqu3z Я пытаюсь следовать вашему
(вы действительно хотите очистить БД для каждого тестового примера? похоже, что только ваш первый тестовый пример будет успешным, другие не будут иметь данных. Но я предполагаю, что вы хотите очистить БД для каждого тестового примера).
Вот две мои попытки решить ваш вариант использования с Mocha как есть:
(1) используйте beforeEach, но переверните логическое значение, поэтому beforeEach запускается только один раз (это отстой)
describe('Create Article', () => {
beforeEach(() => console.log('CLEAN DB'))
context('When Article is valid', () => {
let flip = true;
beforeEach(() => flip && (flip = false; console.log('INSERTING VALID ARTICLE')))
it('should not fail')
it('should have some properties')
it('should send an email')
})
context('When Article is not valid', () => {
let flip = true;
beforeEach(() => flip && (flip = false; console.log('INSERTING NOT VALID ARTICLE')))
it('should fail')
it('should not send an email')
})
})
(2) Другой способ сделать это - не намного лучше!
describe('Create Article', () => {
let cleanDB = function(fn){
return function(done){
actuallyCleanDB(function(err){
if(err) return done(err);
fn(done);
});
}
};
context('When Article is valid', () => {
before(() => console.log('INSERTING VALID ARTICLE'))
it('should not fail', cleanDB(function(done){})
it('should have some properties', cleanDB(function(done){}))
it('should send an email', cleanDB(function(done){}))
})
context('When Article is not valid', () => {
before(() => console.log('INSERTING NOT VALID ARTICLE')))
it('should fail', cleanDB(function(done){}))
it('should not send an email',cleanDB(function(done){}))
})
})
Пожалуйста, дайте мне знать, если я понимаю вашу проблему, и я потрачу больше циклов на обдумывание решения. Ни одно из моих решений не очень хорошее, но я просто хочу убедиться, что понимаю вашу проблему.
@RathaKM
вы регистрируете свои тесты it () асинхронно? Если так, то это запрещено. Вы должны зарегистрировать все describe/after/afterEach/before/beforeEach
синхронно.
// event loop tick X
dataDriven(testData, function () {
// it() must be called in the same event loop tick as X above
it('expecting updated testData for UK', function (ctx, done) {
assert.equal(ctx.country, 'UK');
done();
});
});
Кроме того, откуда берется ctx? Возможно, я не в курсе последних функций Mocha, но я никогда не видел
it(foo, function(ctx, done){
// where is ctx coming from?
});
Я использую тот же сценарий, который описал describe
level beforeEach
Для всех, у кого есть этот общий сценарий, я создал эту вспомогательную функцию, которая перебирает все наборы в текущем блоке и добавляет к каждому из них крючок beforeAll
:
function beforeEachSuite (fn) {
before(function () {
let suites = this.test.parent.suites || []
suites.forEach(s => {
s.beforeAll(fn)
let hook = s._beforeAll.pop()
s._beforeAll.unshift(hook)
})
})
}
Это разблокирует вариант использования, который я описал почти год назад:
describe('Create Article', () => {
beforeEachSuite(() => console.log('CLEAN DB'))
context('When Article is valid', () => {
before(() => console.log("INSERTING VALID ARTICLE"))
it('should not fail')
it('should have some properties')
it('should send an email')
})
context('When Article is not valid', () => {
before(() => console.log("INSERTING NOT VALID ARTICLE"))
it('should fail')
it('should not send an email')
})
})
До сих пор не понимаю, почему этот вариант использования не считается полезным или законным.
Может быть, это решение можно превратить в PR за beforeEachSuite
и afterEachSuite
Да, пожалуйста, нам это нужно.
Есть много вариантов использования, например https://github.com/mochajs/mocha/issues/911#issuecomment -316736991.
+1
+1
Я только что наткнулся на эту ветку в поисках чего-то похожего. Так что всем, кто наткнется на этот пост ...
Я адаптировал подход, описанный @ marqu3z, к его собственному пакету NPM: mocha-suite-hooks
.
Поэтому, если вы оказались в ситуации, когда before
недостаточно, а beforeEach
слишком много, попробуйте beforeSuite
.
Пакет все еще довольно сырой, поэтому мы будем благодарны за любые отзывы!
Самый полезный комментарий
Для всех, у кого есть этот общий сценарий, я создал эту вспомогательную функцию, которая перебирает все наборы в текущем блоке и добавляет к каждому из них крючок
beforeAll
:Это разблокирует вариант использования, который я описал почти год назад:
До сих пор не понимаю, почему этот вариант использования не считается полезным или законным.
Может быть, это решение можно превратить в PR за
beforeEachSuite
иafterEachSuite