Skip to content

add after.always and afterEach.always hooks - fixes #474 #806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ var chainableMethods = {
exclusive: false,
skipped: false,
todo: false,
callback: false
callback: false,
always: false
},
chainableMethods: {
test: {},
Expand All @@ -28,7 +29,8 @@ var chainableMethods = {
only: {exclusive: true},
beforeEach: {type: 'beforeEach'},
afterEach: {type: 'afterEach'},
cb: {callback: true}
cb: {callback: true},
always: {always: true}
}
};

Expand Down
30 changes: 23 additions & 7 deletions lib/test-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ function TestCollection() {
before: [],
beforeEach: [],
after: [],
afterEach: []
afterAlways: [],
afterEach: [],
afterEachAlways: []
};

this._emitTestResult = this._emitTestResult.bind(this);
Expand Down Expand Up @@ -58,13 +60,17 @@ TestCollection.prototype.add = function (test) {
}
}

if (metadata.always && type !== 'after' && type !== 'afterEach') {
throw new Error('"always" can only be used with after and afterEach hooks');
}

// add a hook
if (type !== 'test') {
if (metadata.exclusive) {
throw new Error('"only" cannot be used with a ' + type + ' test');
throw new Error('"only" cannot be used with a ' + type + ' hook');
}

this.hooks[type].push(test);
this.hooks[type + (metadata.always ? 'Always' : '')].push(test);
return;
}

Expand Down Expand Up @@ -151,20 +157,25 @@ TestCollection.prototype._buildTest = function (test, context) {

TestCollection.prototype._buildTestWithHooks = function (test) {
if (test.metadata.skipped) {
return [this._skippedTest(this._buildTest(test))];
return new Sequence([this._skippedTest(this._buildTest(test))], true);
}

var context = {context: {}};

var beforeHooks = this._buildHooks(this.hooks.beforeEach, test.title, context);
var afterHooks = this._buildHooks(this.hooks.afterEach, test.title, context);

return [].concat(beforeHooks, this._buildTest(test, context), afterHooks);
var sequence = new Sequence([].concat(beforeHooks, this._buildTest(test, context), afterHooks), true);
if (this.hooks.afterEachAlways.length !== 0) {
var afterAlwaysHooks = new Sequence(this._buildHooks(this.hooks.afterEachAlways, test.title, context));
sequence = new Sequence([sequence, afterAlwaysHooks], false);
}
return sequence;
};

TestCollection.prototype._buildTests = function (tests) {
return tests.map(function (test) {
return new Sequence(this._buildTestWithHooks(test), true);
return this._buildTestWithHooks(test);
}, this);
};

Expand All @@ -176,5 +187,10 @@ TestCollection.prototype.build = function (bail) {
var concurrentTests = new Concurrent(this._buildTests(this.tests.concurrent), bail);
var allTests = new Sequence([serialTests, concurrentTests]);

return new Sequence([beforeHooks, allTests, afterHooks], true);
var finalTests = new Sequence([beforeHooks, allTests, afterHooks], true);
if (this.hooks.afterAlways.length !== 0) {
var afterAlwaysHooks = new Sequence(this._buildHooks(this.hooks.afterAlways));
finalTests = new Sequence([finalTests, afterAlwaysHooks], false);
}
return finalTests;
};
12 changes: 10 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,9 @@ test.todo('will think about writing this later');

AVA lets you register hooks that are run before and after your tests. This allows you to run setup and/or teardown code.

`test.before()` registers a hook to be run before the first test in your test file. Similarly `test.after()` registers a hook to be run after the last test.
`test.before()` registers a hook to be run before the first test in your test file. Similarly `test.after()` registers a hook to be run after the last test. Use `test.after.always()` to register a hook that will **always** run once your tests and other hooks complete. `.always()` hooks run regardless of whether there were earlier failures, so they are ideal for cleanup tasks.

`test.beforeEach()` registers a hook to be run before each test in your test file. Similarly `test.afterEach()` a hook to be run after each test.
`test.beforeEach()` registers a hook to be run before each test in your test file. Similarly `test.afterEach()` a hook to be run after each test. Use `test.afterEach.always()` to register an after hook that is called even if other test hooks, or the test itself, fail. `.always()` hooks are ideal for cleanup tasks.

Like `test()` these methods take an optional title and a callback function. The title is shown if your hook fails to execute. The callback is called with an [execution object](#t).

Expand All @@ -439,6 +439,10 @@ test.after('cleanup', t => {
// this runs after all tests
});

test.after.always('guaranteed cleanup', t => {
// this will always run, regardless of earlier failures
});

test.beforeEach(t => {
// this runs before each test
});
Expand All @@ -447,6 +451,10 @@ test.afterEach(t => {
// this runs after each test
});

test.afterEach.always(t => {
// this runs after each test and other test hooks, even if they failed
});

test(t => {
// regular test
});
Expand Down
166 changes: 166 additions & 0 deletions test/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,68 @@ test('after', function (t) {
});
});

test('after not run if test failed', function (t) {
t.plan(3);

var runner = new Runner();
var arr = [];

runner.after(function () {
arr.push('a');
});

runner.test(function () {
throw new Error('something went wrong');
});
runner.run({}).then(function (stats) {
t.is(stats.passCount, 0);
t.is(stats.failCount, 1);
t.strictDeepEqual(arr, []);
t.end();
});
});

test('after.always run even if test failed', function (t) {
t.plan(3);

var runner = new Runner();
var arr = [];

runner.after.always(function () {
arr.push('a');
});

runner.test(function () {
throw new Error('something went wrong');
});
runner.run({}).then(function (stats) {
t.is(stats.passCount, 0);
t.is(stats.failCount, 1);
t.strictDeepEqual(arr, ['a']);
t.end();
});
});

test('after.always run even if before failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.before(function () {
throw new Error('something went wrong');
});

runner.after.always(function () {
arr.push('a');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, ['a']);
t.end();
});
});

test('stop if before hooks failed', function (t) {
t.plan(1);

Expand Down Expand Up @@ -223,6 +285,110 @@ test('after each with serial tests', function (t) {
});
});

test('afterEach not run if concurrent tests failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.afterEach(function () {
arr.push('a');
});

runner.test(function () {
throw new Error('something went wrong');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, []);
t.end();
});
});

test('afterEach not run if serial tests failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.afterEach(function () {
arr.push('a');
});

runner.serial(function () {
throw new Error('something went wrong');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, []);
t.end();
});
});

test('afterEach.always run even if concurrent tests failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.afterEach.always(function () {
arr.push('a');
});

runner.test(function () {
throw new Error('something went wrong');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, ['a']);
t.end();
});
});

test('afterEach.always run even if serial tests failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.afterEach.always(function () {
arr.push('a');
});

runner.serial(function () {
throw new Error('something went wrong');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, ['a']);
t.end();
});
});

test('afterEach.always run even if beforeEach failed', function (t) {
t.plan(1);

var runner = new Runner();
var arr = [];

runner.beforeEach(function () {
throw new Error('something went wrong');
});

runner.test(function () {
arr.push('a');
});

runner.afterEach.always(function () {
arr.push('b');
});

runner.run({}).then(function () {
t.strictDeepEqual(arr, ['b']);
t.end();
});
});

test('ensure hooks run only around tests', function (t) {
t.plan(1);

Expand Down
Loading