Browse Source

Try to fail test if assertions are added after test finishes

Though currently this error is likely to get lost unless there is a
pending assertion or `test.cb()` is used.
master
Mark Wubben 8 years ago
parent
commit
09b23e0d5f
  1. 29
      lib/test.js
  2. 27
      test/test.js

29
lib/test.js

@ -101,6 +101,7 @@ class Test {
this.calledEnd = false; this.calledEnd = false;
this.duration = null; this.duration = null;
this.endCallbackFinisher = null; this.endCallbackFinisher = null;
this.finishing = false;
this.pendingAssertions = []; this.pendingAssertions = [];
this.planCount = null; this.planCount = null;
this.startedAt = 0; this.startedAt = 0;
@ -142,15 +143,27 @@ class Test {
} }
countPassedAssertion() { countPassedAssertion() {
if (this.finishing) {
this.saveFirstError(new Error('Assertion passed, but test has already ended'));
}
this.assertCount++; this.assertCount++;
} }
addPendingAssertion(promise) { addPendingAssertion(promise) {
if (this.finishing) {
this.saveFirstError(new Error('Assertion passed, but test has already ended'));
}
this.assertCount++; this.assertCount++;
this.pendingAssertions.push(promise); this.pendingAssertions.push(promise);
} }
addFailedAssertion(error) { addFailedAssertion(error) {
if (this.finishing) {
this.saveFirstError(new Error('Assertion failed, but test has already ended'));
}
this.assertCount++; this.assertCount++;
this.saveFirstError(error); this.saveFirstError(error);
} }
@ -265,12 +278,13 @@ class Test {
} }
finish() { finish() {
this.finishing = true;
this.verifyPlan();
if (this.pendingAssertions.length === 0) { if (this.pendingAssertions.length === 0) {
return this.finishImmediately(); return this.completeFinish();
} }
this.verifyPlan();
// Consume errors, ensuring there are no unhandled rejections. // Consume errors, ensuring there are no unhandled rejections.
const consumedErrors = Promise.all(this.pendingAssertions) const consumedErrors = Promise.all(this.pendingAssertions)
.catch(err => this.saveFirstError(err)); .catch(err => this.saveFirstError(err));
@ -281,9 +295,7 @@ class Test {
} }
// Finish after potential errors from pending assertions have been consumed. // Finish after potential errors from pending assertions have been consumed.
// Note that the plan must be verified again in case a new assertion was return consumedErrors.then(() => this.completeFinish());
// added.
return consumedErrors.then(() => this.finishImmediately());
} }
finishPromised() { finishPromised() {
@ -292,11 +304,6 @@ class Test {
}); });
} }
finishImmediately() {
this.verifyPlan();
return this.completeFinish();
}
completeFinish() { completeFinish() {
this.duration = globals.now() - this.startedAt; this.duration = globals.now() - this.startedAt;
globals.clearTimeout(this.timeoutHandle); globals.clearTimeout(this.timeoutHandle);

27
test/test.js

@ -508,21 +508,40 @@ test('multiple resolving and rejecting promises passed to t.throws/t.notThrows',
}); });
}); });
test('number of assertions matches t.plan when the test exits, but before all promises resolve another is added', t => { test('number of assertions matches t.plan when the test exits, but before all pending assertions resolve another is added', t => {
let result; let result;
ava(a => { ava(a => {
a.plan(2); a.plan(2);
a.throws(delay.reject(10, new Error('foo')), 'foo'); a.throws(delay.reject(10, new Error('foo')), 'foo');
a.notThrows(delay(10), 'foo'); a.notThrows(delay(10), 'foo');
setTimeout(() => { setTimeout(() => {
a.throws(Promise.reject(new Error('foo')), 'foo'); a.pass();
}, 5); }, 5);
}, null, r => { }, null, r => {
result = r; result = r;
}).run().then(passed => { }).run().then(passed => {
t.is(passed, false); t.is(passed, false);
t.is(result.reason.assertion, 'plan'); t.match(result.reason.message, /Assertion passed, but test has already ended/);
t.is(result.reason.operator, '==='); t.is(result.reason.name, 'Error');
t.end();
});
});
test('number of assertions matches t.plan when the test exits, but before all pending assertions resolve, a failing assertion is added', t => {
let result;
ava(a => {
a.plan(2);
a.throws(delay.reject(10, new Error('foo')), 'foo');
a.notThrows(delay(10), 'foo');
setTimeout(() => {
a.fail();
}, 5);
}, null, r => {
result = r;
}).run().then(passed => {
t.is(passed, false);
t.match(result.reason.message, /Assertion failed, but test has already ended/);
t.is(result.reason.name, 'Error');
t.end(); t.end();
}); });
}); });

Loading…
Cancel
Save