Es gibt keine Dokumentation, _wenn_ ein beforeEach
oder afterEach
wird. Meine Intuition sagt, dass es before
/ after
jeden describe
/ it
Block im aktuellen Kontext ausgeführt werden sollte.
Das Verhalten, das ich jedoch bemerke, ist, dass beforeEach
und afterEach
werden before
/ after
_jeder_ it
Block im aktuellen Kontext und alle verschachtelten Kontexte.
Ich habe einen Machbarkeitsnachweis erstellt, um zu zeigen, dass das festgestellte Verhalten auftritt: https://gist.github.com/twolfson/5883057#file -test-js
Der Einfachheit halber werde ich das Skript kopieren / einfügen und hier ausgeben:
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!
Das Verhalten, das ich intuitiv erwarte, ist, dass afterEach
einmal ausgeführt wird, nach dem second nested it
.
before run!
nested before run!
nested it run!
second nested it run!
afterEach run!
Mein Anwendungsfall für die intuitive Funktionalität besteht darin, den this
Kontext zu löschen, nachdem ein Kontext abgeschlossen ist. Die aktuelle Implementierung bereinigt den Kontext nach jeder Assertion.
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');
});
});
});
Ein vergleichbares Beispiel hierfür wäre das Öffnen einer Datenbanktransaktion in jedem neuen Kontext einer Testsuite.
Ich kann es umgehen, indem ich after
in jedem verschachtelten Kontext aufrufe, aber ich habe das Gefühl, dass es einen Haken im Framework geben könnte.
Für Neugierige ist dies die Problemumgehung für meinen Anwendungsfall:
// 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
nach jeder Runnable
Instanz ausgeführt; in Ihrem Fall ein it()
Block. Wenn Sie das geschachtelte Verhalten nicht wünschen, verschachteln Sie Ihre Tests nicht. Dieses Verhalten zu ändern steht nicht auf dem Tisch.
Wenn Ihr zweiter Test von etwas abhängt, das in Ihrem ersten Test definiert wurde, sind Ihre Tests schmutzig und Sie machen es falsch. Die Reihenfolge, in der Ihre Tests ausgeführt werden, sollte keine Rolle spielen. Schreiben Sie Ihre Tests entsprechend, und sie werden wertvoller.
Außerdem ist es nicht notwendig, in 90 % der Fälle etwas auf this
zu setzen.
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');
});
});
});
Gibt es eine Möglichkeit, Hook-Methoden in der describe()-Ebene anstelle der it()-Ebene auszuführen? Es kann in einigen Szenarien nützlich sein.
@RathaKM Was ist der Anwendungsfall?
Ich bin mir nicht sicher, ob dies ein gutes Beispiel ist, aber wäre das Folgende ein guter Anwendungsfall dafür? @RathaKM gerne korrigieren.
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 : danke für deine Kommentare und bring sie zurück. :) Ihr Beispiel ist das, was logischerweise in der Beschreibungsebene (der BeforeEach) funktioniert. Dies könnte jedoch mit 'before()' in jedem Beschreibungstext erreicht werden.
Das Problem, mit dem ich konfrontiert war, ist im folgenden Beispiel angegeben.
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();
});
});
});
});
Hier schlägt die Testsuite fehl. Wir aktualisieren die _testData_ innerhalb der Hook-Methode 'before()', die direkt vor dem '_it_'-Block aufgerufen wird. Wir verwenden jedoch die Variable _testData_ innerhalb von _dataDriven()_, die ausgeführt wird, bevor der '_it_'-Block ausgeführt wird.
Was wir hier also brauchen, ist ein Ort, um die Variable zu aktualisieren, bevor die Ausführung des _'it'_-Blocks beginnt. Vielleicht brauchen wir die _before()_ Hook-Methodenausführung kurz vor dem 'describe'-Block (describe level Hook-Methode).
Hoffe, das Thema ist jetzt klar.
Gab es hierzu zusätzliche Diskussionen?
warum bekommt man sowas nicht?
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
Block.it
Blöcken einIst das nicht ein legitimer Anwendungsfall?
@marqu3z Ich versuche, Ihrem Anwendungsfall zu folgen. Es ist legitim. Es klingt wie das Problem ist: der Before-Hook läuft vor dem beforeEach-Hook? aber Sie müssen den Before-Hook nach dem beforeEach-Hook ausführen?
(Möchten Sie wirklich die DB für jeden Testfall bereinigen? Scheint, als würde nur Ihr erster Testfall erfolgreich sein, die anderen haben keine Daten. Aber ich gehe davon aus, dass Sie die DB für jeden Testfall bereinigen möchten).
Hier sind meine beiden Versuche, Ihren Anwendungsfall zu lösen, wenn Mocha so ist:
(1) benutze ein beforeEach, aber drehe einen Boolean, so dass beforeEach nur einmal läuft (das ist irgendwie scheiße)
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) Der andere Weg, dies zu tun - das ist nicht viel besser!
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){}))
})
})
Bitte lassen Sie es mich wissen, wenn ich Ihr Problem verstehe, und ich werde weitere Zyklen damit verbringen, über eine Lösung nachzudenken. Keine meiner Lösungen ist sehr gut, aber ich möchte nur sicherstellen, dass ich Ihr Problem verstehe.
@RathaKM
Registrieren Sie Ihre it()-Testfälle asynchron? Wenn ja, ist das nicht erlaubt. Sie müssen describe/after/afterEach/before/beforeEach
alle synchron registrieren.
// 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();
});
});
Und woher kommt ctx? Vielleicht bin ich nicht auf dem neuesten Stand der Mocha-Funktionen, aber ich habe es noch nie gesehen
it(foo, function(ctx, done){
// where is ctx coming from?
});
Ich bin in genau dem gleichen Szenario wie @marqu3z beschrieben. Würde gerne Unterstützung für ein describe
Level beforeEach
Für alle mit diesem allgemeinen Szenario habe ich diese Hilfsfunktion erstellt, die über alle Suiten im aktuellen Block iteriert und jedem von ihnen einen beforeAll
Hook hinzufügt:
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)
})
})
}
Dies entsperrt den Anwendungsfall, den ich vor fast einem Jahr beschreibe:
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')
})
})
Verstehe immer noch nicht, warum dieser Anwendungsfall nicht als nützlich oder legitim angesehen wird.
Vielleicht kann diese Lösung in eine PR für beforeEachSuite
und afterEachSuite
Ja, bitte, wir brauchen das.
Es gibt viele Anwendungsfälle wie https://github.com/mochajs/mocha/issues/911#issuecomment -316736991
+1
+1
Ich bin gerade über diesen Thread gestolpert und habe nach etwas ähnlichem gesucht. Also an alle anderen, die über diesen Beitrag stolpern...
Ich habe den Ansatz, den @marqu3z skizziert hat, an sein eigenes NPM-Paket angepasst: mocha-suite-hooks
.
Wenn Sie sich also in einer Situation befinden, in der before
nicht ausreicht und beforeEach
zu viel ist, versuchen Sie es mit beforeSuite
.
Das Paket ist noch ziemlich roh, daher wäre jedes Feedback sehr dankbar!
Hilfreichster Kommentar
Für alle mit diesem allgemeinen Szenario habe ich diese Hilfsfunktion erstellt, die über alle Suiten im aktuellen Block iteriert und jedem von ihnen einen
beforeAll
Hook hinzufügt:Dies entsperrt den Anwendungsfall, den ich vor fast einem Jahr beschreibe:
Verstehe immer noch nicht, warum dieser Anwendungsfall nicht als nützlich oder legitim angesehen wird.
Vielleicht kann diese Lösung in eine PR für
beforeEachSuite
undafterEachSuite