Skip to content

Commit

Permalink
add after.always and afterEach.always hooks (#806)
Browse files Browse the repository at this point in the history
Fixes #474

* add after.always, afterEach.always hook

* update readme about always modifier

* add test for verifying throw

* add tests for verifying always hook are added

* update readme for better "always" explaination

* fix typo in test/test-collection.js

* better always checking and implement

* modify error message for only

* fix build error

* add always to default in test
  • Loading branch information
cgcgbcbc authored and jamestalmage committed May 8, 2016
1 parent 5f83135 commit 61f0958
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 16 deletions.
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

0 comments on commit 61f0958

Please sign in to comment.