Skip to content
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

Add a description with the reason why a test is pending #3324

Closed
wants to merge 12 commits into from
14 changes: 14 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,20 @@ before(function() {

> Before Mocha v3.0.0, `this.skip()` was not supported in asynchronous tests and hooks.

You may provide a `reason` argument to `this.skip()` which can be helpful in self documenting test results.

```js
it('should only test in the correct environment', function() {
if (/* check test environment */) {
// make assertions
} else {
this.skip('the environment is not correct');
}
});
```

*Note*: The `reason` argument is only supported by `this.skip()`, not `.skip()`. If you want to mark a test as pending with a reason, it must be done at runtime.

## Retry Tests

You can choose to retry failed tests up to a certain number of times. This feature is designed to handle end-to-end tests (functional tests/Selenium...) where resources cannot be easily mocked/stubbed. **It's not recommended to use this feature for unit tests**.
Expand Down
5 changes: 3 additions & 2 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ Context.prototype.slow = function(ms) {
*
* @api private
* @throws Pending
* @param {string} reason
*/
Context.prototype.skip = function() {
this.runnable().skip();
Context.prototype.skip = function(reason) {
this.runnable().skip(reason);
};

/**
Expand Down
9 changes: 8 additions & 1 deletion lib/reporters/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,25 @@ function JSONReporter(runner) {
* @return {Object}
*/
function clean(test) {
var res;
var err = test.err || {};
if (err instanceof Error) {
err = errorJSON(err);
}

return {
res = {
title: test.title,
fullTitle: test.fullTitle(),
duration: test.duration,
currentRetry: test.currentRetry(),
err: cleanCycles(err)
};

if (test.reason) {
res.reason = test.reason;
}

return res;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/reporters/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ function Spec(runner) {

runner.on('pending', function(test) {
var fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
console.log(
fmt,
test.title + (test.reason ? ' (' + test.reason + ')' : '')
);
});

runner.on('pass', function(test) {
Expand Down
8 changes: 7 additions & 1 deletion lib/reporters/xunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,13 @@ XUnit.prototype.test = function(test) {
)
);
} else if (test.isPending()) {
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
var skippedAttrs = {};
if (test.reason) {
skippedAttrs.message = test.reason;
}
this.write(
tag('testcase', attrs, false, tag('skipped', skippedAttrs, true))
);
} else {
this.write(tag('testcase', attrs, true));
}
Expand Down
17 changes: 13 additions & 4 deletions lib/runnable.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,14 @@ Runnable.prototype.enableTimeouts = function(enabled) {
* @memberof Mocha.Runnable
* @public
* @api public
* @param {string} reason - Reason the Runnable is being skipped.
*/
Runnable.prototype.skip = function() {
throw new Pending('sync skip');
Runnable.prototype.skip = function(reason) {
if (reason) {
reason = String(reason);
this.reason = reason;
}
throw new Pending(reason);
};

/**
Expand Down Expand Up @@ -327,8 +332,12 @@ Runnable.prototype.run = function(fn) {
this.resetTimeout();

// allows skip() to be used in an explicit async context
this.skip = function asyncSkip() {
done(new Pending('async skip call'));
this.skip = function asyncSkip(reason) {
if (reason) {
reason = String(reason);
this.reason = reason;
}
done(new Pending(reason));
// halt execution. the Runnable will be marked pending
// by the previous call, and the uncaught handler will ignore
// the failure.
Expand Down
6 changes: 6 additions & 0 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,15 @@ Runner.prototype.hook = function(name, fn) {
if (err instanceof Pending) {
if (name === 'beforeEach' || name === 'afterEach') {
self.test.pending = true;
if (err.message) {
self.test.reason = err.message;
}
} else {
suite.tests.forEach(function(test) {
test.pending = true;
if (err.message) {
test.reason = err.message;
}
});
// a pending hook won't be executed twice.
hook.pending = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

describe('skip in before with reason', function () {
before(function (done) {
var self = this;
setTimeout(function () {
self.skip('skip reason');
}, 50);
});

it('should never run this test', function () {
throw new Error('never thrown');
});

it('should never run this test', function () {
throw new Error('never thrown');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

describe('skip in beforeEach with reason', function () {
beforeEach(function (done) {
var self = this;
setTimeout(function () {
self.skip('skip reason');
}, 50);
});

it('should never run this test', function () {
throw new Error('never thrown');
});

it('should never run this test', function () {
throw new Error('never thrown');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

describe('skip in test with reason', function () {
it('should skip async with reason', function (done) {
var self = this;
setTimeout(function () {
self.skip('skip reason');
}, 50);
});

it('should run other tests in the suite', function () {
// Do nothing
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

describe('skip in before with reason', function () {
before(function () {
this.skip('skip reason');
});

it('should never run this test', function () {
throw new Error('never thrown');
});

it('should never run this test', function () {
throw new Error('never thrown');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

describe('skip in beforeEach with reason', function () {
beforeEach(function () {
this.skip('skip reason');
});

it('should never run this test', function () {
throw new Error('never thrown');
});

it('should never run this test', function () {
throw new Error('never thrown');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

describe('skip in test with reason', function () {
it('should skip immediately with reason', function () {
this.skip('skip reason');
throw new Error('never thrown');
});

it('should run other tests in the suite', function () {
// Do nothing
});
});
120 changes: 120 additions & 0 deletions test/integration/pending.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ describe('pending', function() {
assert.equal(res.stats.passes, 1);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.ok(!res.pending[0].hasOwnProperty('reason'));
done();
});
});

it('should allow a skip reason', function(done) {
run('pending/skip-sync-spec-with-reason.fixture.js', args, function(
err,
res
) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 1);
assert.equal(res.stats.passes, 1);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
done();
});
});
Expand All @@ -80,6 +99,25 @@ describe('pending', function() {
done();
});
});

it('should allow a skip reason', function(done) {
run('pending/skip-sync-before-with-reason.fixture.js', args, function(
err,
res
) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 2);
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
assert.equal(res.pending[1].reason, 'skip reason');
done();
});
});
});

describe('in beforeEach', function() {
Expand All @@ -99,6 +137,26 @@ describe('pending', function() {
done();
});
});

it('should allow a skip reason', function(done) {
run(
'pending/skip-sync-beforeEach-with-reason.fixture.js',
args,
function(err, res) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 2);
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
assert.equal(res.pending[1].reason, 'skip reason');
done();
}
);
});
});
});

Expand All @@ -114,6 +172,25 @@ describe('pending', function() {
assert.equal(res.stats.passes, 1);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.ok(!res.pending[0].hasOwnProperty('reason'));
done();
});
});

it('should allow a skip reason', function(done) {
run('pending/skip-async-spec-with-reason.fixture.js', args, function(
err,
res
) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 1);
assert.equal(res.stats.passes, 1);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
done();
});
});
Expand All @@ -130,6 +207,27 @@ describe('pending', function() {
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.ok(!res.pending[0].hasOwnProperty('reason'));
assert.ok(!res.pending[1].hasOwnProperty('reason'));
done();
});
});

it('should allow a skip reason', function(done) {
run('pending/skip-async-before-with-reason.fixture.js', args, function(
err,
res
) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 2);
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
assert.equal(res.pending[1].reason, 'skip reason');
done();
});
});
Expand All @@ -149,9 +247,31 @@ describe('pending', function() {
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.ok(!res.pending[0].hasOwnProperty('reason'));
assert.ok(!res.pending[1].hasOwnProperty('reason'));
done();
});
});

it('should allow a skip reason', function(done) {
run(
'pending/skip-sync-beforeEach-with-reason.fixture.js',
args,
function(err, res) {
if (err) {
done(err);
return;
}
assert.equal(res.stats.pending, 2);
assert.equal(res.stats.passes, 0);
assert.equal(res.stats.failures, 0);
assert.equal(res.code, 0);
assert.equal(res.pending[0].reason, 'skip reason');
assert.equal(res.pending[1].reason, 'skip reason');
done();
}
);
});
});
});
});