From 2da50aa53d76cb111dbb5fe952bd0beb49f2e278 Mon Sep 17 00:00:00 2001 From: Christopher Hiller Date: Mon, 8 Jun 2020 15:48:40 -0700 Subject: [PATCH] add a section regarding 3p reporters & parallel mode also rename "Migration Checklist" to "Troubleshooting Parallel Mode" --- docs/index.md | 310 +++++++++++++++++++++++++------------------------- 1 file changed, 157 insertions(+), 153 deletions(-) diff --git a/docs/index.md b/docs/index.md index 29c02b4061..5d4cbdc87d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -77,9 +77,9 @@ In your editor: ```js var assert = require('assert'); -describe('Array', function() { - describe('#indexOf()', function() { - it('should return -1 when the value is not present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it('should return -1 when the value is not present', function () { assert.equal([1, 2, 3].indexOf(4), -1); }); }); @@ -159,7 +159,7 @@ spawn child process If you use callback-based async tests, Mocha will throw an error if `done()` is called multiple times. This is handy for catching accidental double callbacks. ```javascript -it('double done', function(done) { +it('double done', function (done) { // Calling `done()` twice is an error setImmediate(done); setImmediate(done); @@ -202,11 +202,11 @@ Mocha allows you to use any assertion library you wish. In the above example, we By adding an argument (usually named `done`) to `it()` to a test callback, Mocha will know that it should wait for this function to be called to complete the test. This callback accepts both an `Error` instance (or subclass thereof) _or_ a falsy value; anything else is invalid usage and throws an error (usually causing a failed test). ```js -describe('User', function() { - describe('#save()', function() { - it('should save without error', function(done) { +describe('User', function () { + describe('#save()', function () { + it('should save without error', function (done) { var user = new User('Luna'); - user.save(function(err) { + user.save(function (err) { if (err) done(err); else done(); }); @@ -218,9 +218,9 @@ describe('User', function() { Alternatively, use the `done()` callback directly (which will handle an error argument, if it exists): ```js -describe('User', function() { - describe('#save()', function() { - it('should save without error', function(done) { +describe('User', function () { + describe('#save()', function () { + it('should save without error', function (done) { var user = new User('Luna'); user.save(done); }); @@ -233,14 +233,14 @@ describe('User', function() { Alternately, instead of using the `done()` callback, you may return a [Promise][mdn-promise]. This is useful if the APIs you are testing return promises instead of taking callbacks: ```js -beforeEach(function() { - return db.clear().then(function() { +beforeEach(function () { + return db.clear().then(function () { return db.save([tobi, loki, jane]); }); }); -describe('#find()', function() { - it('respond with matching records', function() { +describe('#find()', function () { + it('respond with matching records', function () { return db.find({type: 'User'}).should.eventually.have.length(3); }); }); @@ -254,8 +254,8 @@ In Mocha v3.0.0 and newer, returning a `Promise` _and_ calling `done()` will res const assert = require('assert'); // antipattern -it('should complete this test', function(done) { - return new Promise(function(resolve) { +it('should complete this test', function (done) { + return new Promise(function (resolve) { assert.ok(true); resolve(); }).then(done); @@ -269,13 +269,13 @@ The above test will fail with `Error: Resolution method is overspecified. Specif If your JS environment supports [async / await][mdn-async], you can also write asynchronous tests like this: ```js -beforeEach(async function() { +beforeEach(async function () { await db.clear(); await db.save([tobi, loki, jane]); }); -describe('#find()', function() { - it('responds with matching records', async function() { +describe('#find()', function () { + it('responds with matching records', async function () { const users = await db.find({type: 'User'}); users.should.have.length(3); }); @@ -287,9 +287,9 @@ describe('#find()', function() { When testing synchronous code, omit the callback and Mocha will automatically continue on to the next test. ```js -describe('Array', function() { - describe('#indexOf()', function() { - it('should return -1 when the value is not present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it('should return -1 when the value is not present', function () { [1, 2, 3].indexOf(5).should.equal(-1); [1, 2, 3].indexOf(0).should.equal(-1); }); @@ -318,20 +318,20 @@ _If you do not need to use_ Mocha's context, lambdas should work. Be aware that With its default "BDD"-style interface, Mocha provides the hooks `before()`, `after()`, `beforeEach()`, and `afterEach()`. These should be used to set up preconditions and clean up after your tests. ```js -describe('hooks', function() { - before(function() { +describe('hooks', function () { + before(function () { // runs once before the first test in this block }); - after(function() { + after(function () { // runs once after the last test in this block }); - beforeEach(function() { + beforeEach(function () { // runs before each test in this block }); - afterEach(function() { + afterEach(function () { // runs after each test in this block }); @@ -346,7 +346,7 @@ describe('hooks', function() { Any hook can be invoked with an optional description, making it easier to pinpoint errors in your tests. If a hook is given a named function, that name will be used if no description is supplied. ```js -beforeEach(function() { +beforeEach(function () { // beforeEach hook }); @@ -354,7 +354,7 @@ beforeEach(function namedFun() { // beforeEach:namedFun }); -beforeEach('some description', function() { +beforeEach('some description', function () { // beforeEach:some description }); ``` @@ -364,22 +364,22 @@ beforeEach('some description', function() { All hooks (`before()`, `after()`, `beforeEach()`, `afterEach()`) may be sync or async as well, behaving much like a regular test-case. For example, you may wish to populate database with dummy content before each test: ```js -describe('Connection', function() { +describe('Connection', function () { var db = new Connection(), tobi = new User('tobi'), loki = new User('loki'), jane = new User('jane'); - beforeEach(function(done) { - db.clear(function(err) { + beforeEach(function (done) { + db.clear(function (err) { if (err) return done(err); db.save([tobi, loki, jane], done); }); }); - describe('#find()', function() { - it('respond with matching records', function(done) { - db.find({type: 'User'}, function(err, res) { + describe('#find()', function () { + it('respond with matching records', function (done) { + db.find({type: 'User'}, function (err, res) { if (err) return done(err); res.should.have.length(3); done(); @@ -393,7 +393,7 @@ describe('Connection', function() { A hook defined at the top scope of a test file (outside of a suite) is a _root hook_. -As of v8.0.0, [Root Hook Plugins](#root-hook-plugins) are the preferred mechanism for setting root-level hooks. +As of v8.0.0, [Root Hook Plugins](#root-hook-plugins) are the preferred mechanism for setting root hooks. ### Delayed Root Suite @@ -402,10 +402,10 @@ As of v8.0.0, [Root Hook Plugins](#root-hook-plugins) are the preferred mechanis If you need to perform asynchronous operations before any of your suites are run, you may delay the root suite. Run `mocha` with the `--delay` flag. This will attach a special callback function, `run()`, to the global context: ```js -setTimeout(function() { +setTimeout(function () { // do some setup - describe('my suite', function() { + describe('my suite', function () { // ... }); @@ -418,8 +418,8 @@ setTimeout(function() { "Pending"--as in "someone should write these test cases eventually"--test-cases are those _without_ a callback: ```js -describe('Array', function() { - describe('#indexOf()', function() { +describe('Array', function () { + describe('#indexOf()', function () { // pending test below it('should return -1 when the value is not present'); }); @@ -438,8 +438,8 @@ The exclusivity feature allows you to run _only_ the specified suite or test-cas by appending `.only()` to the function. Here's an example of executing only a particular suite: ```js -describe('Array', function() { - describe.only('#indexOf()', function() { +describe('Array', function () { + describe.only('#indexOf()', function () { // ... }); }); @@ -450,13 +450,13 @@ _Note_: All nested suites will still be executed. Here's an example of executing an individual test case: ```js -describe('Array', function() { - describe('#indexOf()', function() { - it.only('should return -1 unless present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it.only('should return -1 unless present', function () { // ... }); - it('should return the index when present', function() { + it('should return the index when present', function () { // ... }); }); @@ -466,17 +466,17 @@ describe('Array', function() { Previous to v3.0.0, `.only()` used string matching to decide which tests to execute; this is no longer the case. In v3.0.0 or newer, `.only()` can be used multiple times to define a subset of tests to run: ```js -describe('Array', function() { - describe('#indexOf()', function() { - it.only('should return -1 unless present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it.only('should return -1 unless present', function () { // this test will be run }); - it.only('should return the index when present', function() { + it.only('should return the index when present', function () { // this test will also be run }); - it('should return -1 if called with a non-Array context', function() { + it('should return -1 if called with a non-Array context', function () { // this test will not be run }); }); @@ -486,25 +486,25 @@ describe('Array', function() { You may also choose multiple suites: ```js -describe('Array', function() { - describe.only('#indexOf()', function() { - it('should return -1 unless present', function() { +describe('Array', function () { + describe.only('#indexOf()', function () { + it('should return -1 unless present', function () { // this test will be run }); - it('should return the index when present', function() { + it('should return the index when present', function () { // this test will also be run }); }); - describe.only('#concat()', function() { - it('should return a new Array', function() { + describe.only('#concat()', function () { + it('should return a new Array', function () { // this test will also be run }); }); - describe('#slice()', function() { - it('should return a new Array', function() { + describe('#slice()', function () { + it('should return a new Array', function () { // this test will not be run }); }); @@ -514,13 +514,13 @@ describe('Array', function() { But _tests will have precedence_: ```js -describe('Array', function() { - describe.only('#indexOf()', function() { - it.only('should return -1 unless present', function() { +describe('Array', function () { + describe.only('#indexOf()', function () { + it.only('should return -1 unless present', function () { // this test will be run }); - it('should return the index when present', function() { + it('should return the index when present', function () { // this test will not be run }); }); @@ -536,13 +536,13 @@ _Note_: Hooks, if present, will still be executed. This feature is the inverse of `.only()`. By appending `.skip()`, you may tell Mocha to ignore test case(s). Anything skipped will be marked as [pending](#pending-tests), and reported as such. Here's an example of skipping an individual test: ```js -describe('Array', function() { - describe('#indexOf()', function() { - it.skip('should return -1 unless present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it.skip('should return -1 unless present', function () { // this test will not be run }); - it('should return the index when present', function() { + it('should return the index when present', function () { // this test will be run }); }); @@ -552,9 +552,9 @@ describe('Array', function() { You can also put `.skip()` on an entire suite. This is equivalent to appending `.skip()` onto all tests in the suite. Hooks in the suite are also skipped. ```js -describe('Array', function() { - describe.skip('#indexOf()', function() { - it('should return -1 unless present', function() { +describe('Array', function () { + describe.skip('#indexOf()', function () { + it('should return -1 unless present', function () { // this test will not be run }); }); @@ -612,21 +612,21 @@ before(function() { This will skip all `it`, `beforeEach/afterEach`, and `describe` blocks within the suite. `before/after` hooks are skipped unless they are defined at the same level as the hook containing `this.skip()`. ```js -describe('outer', function() { - before(function() { +describe('outer', function () { + before(function () { this.skip(); }); - after(function() { + after(function () { // will be executed }); - describe('inner', function() { - before(function() { + describe('inner', function () { + before(function () { // will be skipped }); - after(function() { + after(function () { // will be skipped }); }); @@ -646,15 +646,15 @@ This feature does re-run a failed test and its corresponding `beforeEach/afterEa **NOTE**: Example below was written using Selenium webdriver (which [overwrites global Mocha hooks][selenium-webdriver-testing] for `Promise` chain). ```js -describe('retries', function() { +describe('retries', function () { // Retry all tests in this suite up to 4 times this.retries(4); - beforeEach(function() { + beforeEach(function () { browser.get('http://www.yahoo.com'); }); - it('should succeed on the 3rd try', function() { + it('should succeed on the 3rd try', function () { // Specify this test to only retry up to 2 times this.retries(2); expect($('.foo').isDisplayed()).to.eventually.be.true; @@ -672,20 +672,20 @@ Take the following example: var assert = require('chai').assert; function add() { - return Array.prototype.slice.call(arguments).reduce(function(prev, curr) { + return Array.prototype.slice.call(arguments).reduce(function (prev, curr) { return prev + curr; }, 0); } -describe('add()', function() { +describe('add()', function () { var tests = [ {args: [1, 2], expected: 3}, {args: [1, 2, 3], expected: 6}, - {args: [1, 2, 3, 4], expected: 10} + {args: [1, 2, 3, 4], expected: 10}, ]; - tests.forEach(function(test) { - it('correctly adds ' + test.args.length + ' args', function() { + tests.forEach(function (test) { + it('correctly adds ' + test.args.length + ' args', function () { var res = add.apply(null, test.args); assert.equal(res, test.expected); }); @@ -721,10 +721,10 @@ There are three levels of test duration (depicted in the following image): To tweak what's considered "slow", you can use the `slow()` method: ```js -describe('something slow', function() { +describe('something slow', function () { this.slow(300000); // five minutes - it('should take long enough for me to go make a sandwich', function() { + it('should take long enough for me to go make a sandwich', function () { // ... }); }); @@ -737,14 +737,14 @@ describe('something slow', function() { Suite-level timeouts may be applied to entire test "suites", or disabled via `this.timeout(0)`. This will be inherited by all nested suites and test-cases that do not override the value. ```js -describe('a suite of tests', function() { +describe('a suite of tests', function () { this.timeout(500); - it('should take less than 500ms', function(done) { + it('should take less than 500ms', function (done) { setTimeout(done, 300); }); - it('should take less than 500ms as well', function(done) { + it('should take less than 500ms as well', function (done) { setTimeout(done, 250); }); }); @@ -755,7 +755,7 @@ describe('a suite of tests', function() { Test-specific timeouts may also be applied, or the use of `this.timeout(0)` to disable timeouts all together: ```js -it('should take less than 500ms', function(done) { +it('should take less than 500ms', function (done) { this.timeout(500); setTimeout(done, 300); }); @@ -766,8 +766,8 @@ it('should take less than 500ms', function(done) { Hook-level timeouts may also be applied: ```js -describe('a suite of tests', function() { - beforeEach(function(done) { +describe('a suite of tests', function () { + beforeEach(function (done) { this.timeout(3000); // A very long environment setup. setTimeout(done, 2500); }); @@ -1065,17 +1065,17 @@ Cause Mocha to only run tests matching the given `regexp`, which is internally c Suppose, for example, you have "api" related tests, as well as "app" related tests, as shown in the following snippet; One could use `--grep api` or `--grep app` to run one or the other. The same goes for any other part of a suite or test-case title, `--grep users` would be valid as well, or even `--grep GET`. ```js -describe('api', function() { - describe('GET /api/users', function() { - it('respond with an array of users', function() { +describe('api', function () { + describe('GET /api/users', function () { + it('respond with an array of users', function () { // ... }); }); }); -describe('app', function() { - describe('GET /users', function() { - it('respond with an array of users', function() { +describe('app', function () { + describe('GET /users', function () { + it('respond with an array of users', function () { // ... }); }); @@ -1215,12 +1215,12 @@ A _root hook_ is a hook in a test file which is _not defined_ within a suite. An // test/setup.js // root hook to run before every test (even in other files) -beforeEach(function() { +beforeEach(function () { doMySetup(); }); // root hook to run after every test (even in other files) -afterEach(function() { +afterEach(function () { doMyTeardown(); }); ``` @@ -1246,15 +1246,19 @@ Here are a couple suggested workarounds: Parallel mode is only available in Node.js, for now. -### Migration Checklist +### Limited Reporter API for Third-Party Reporters + +Third-party reporters may encounter issues when attempting to access non-existent properties within `Test`, `Suite`, and `Hook` objects. If a third-party reporter does not work in parallel mode (but otherwise works in serial mode), please [file an issue](https://github.com/mochajs/mocha/issues/new). + +### Troubleshooting Parallel Mode If you find your tests don't work properly when run with [`--parallel`](#-parallel-p), either shrug and move on, or use this handy-dandy checklist to get things working: - :white_check_mark: Ensure you are using a [supported reporter](#reporter-limitations). - :white_check_mark: Ensure you are not using [other unsupported flags](#file-order-is-non-deterministic). - :white_check_mark: Double-check your [config file](#configuring-mocha-nodejs); options set in config files will be merged with any command-line option. -- :white_check_mark: Look for root-level hooks (they look like [this](#root-hooks-are-not-global)) in your tests. Move them into a [root-level hook plugin](#root-hook-plugins). -- :white_check_mark: Do any assertion, mock, or other test libraries you're consuming use root-level hooks? They may need to be [migrated](#migrating-a-library-to-use-root-hook-plugins) for compatibility with parallel mode. +- :white_check_mark: Look for root hooks (they look like [this](#root-hooks-are-not-global)) in your tests. Move them into a [Root Hook Plugin](#root-hook-plugins). +- :white_check_mark: Do any assertion, mock, or other test libraries you're consuming use root hooks? They may need to be [migrated](#migrating-a-library-to-use-root-hook-plugins) for compatibility with parallel mode. - :white_check_mark: If tests are unexpectedly timing out, you may need to increase the default test timeout (via [`--timeout`](#-timeout-ms-t-ms)) - :white_check_mark: Ensure your tests do not depend on being run in a specific order. - :white_check_mark: Ensure your tests clean up after themselves; remove temp files, handles, sockets, etc. Don't try to share state or resources between test files. @@ -1290,7 +1294,7 @@ exports.mochaHooks = { beforeEach(done) { // do something before every test done(); - } + }, }; ``` @@ -1307,7 +1311,7 @@ export const mochaHooks = { beforeEach(done) { // do something before every test done(); - } + }, }; ``` @@ -1343,7 +1347,7 @@ export const mochaHooks = { if (require('os').userInfo().username === 'bob') { return this.skip(); } - } + }, }; ``` @@ -1356,14 +1360,14 @@ Multiple root hooks can be defined in a single plugin, for organizational purpos export const mochaHooks = { beforeEach: [ - function(done) { + function (done) { // do something before every test, // then run the next hook in this array }, - async function() { + async function () { // async or Promise-returning functions allowed - } - ] + }, + ], }; ``` @@ -1379,20 +1383,20 @@ export const mochaHooks = () => { // root hooks object return { beforeEach: [ - function() { + function () { // CI-specific beforeEach }, - function() { + function () { // some other CI-specific beforeEach - } - ] + }, + ], }; } // root hooks object return { beforeEach() { // regular beforeEach - } + }, }; }; ``` @@ -1410,7 +1414,7 @@ export const mochaHooks = async () => { return { beforeEach() { // something - } + }, }; } }; @@ -1435,16 +1439,16 @@ For example, given the following file, `test/test.spec.js`, containing root hook ```js // test/test.spec.js -beforeEach(function() { +beforeEach(function () { // global setup for all tests }); -after(function() { +after(function () { // one-time final cleanup }); -describe('my test suite', function() { - it('should have run my global setup', function() { +describe('my test suite', function () { + it('should have run my global setup', function () { // make assertion }); }); @@ -1472,8 +1476,8 @@ Your original `test/test.spec.js` should now contain: ```js // test/test.spec.js -describe('my test suite', function() { - it('should have run my global setup', function() { +describe('my test suite', function () { + it('should have run my global setup', function () { // make assertion }); }); @@ -1502,24 +1506,24 @@ The **BDD** interface provides `describe()`, `context()`, `it()`, `specify()`, ` > All of the previous examples were written using the **BDD** interface. ```js -describe('Array', function() { - before(function() { +describe('Array', function () { + before(function () { // ... }); - describe('#indexOf()', function() { - context('when not present', function() { - it('should not throw an error', function() { - (function() { + describe('#indexOf()', function () { + context('when not present', function () { + it('should not throw an error', function () { + (function () { [1, 2, 3].indexOf(4); }.should.not.throw()); }); - it('should return -1', function() { + it('should return -1', function () { [1, 2, 3].indexOf(4).should.equal(-1); }); }); - context('when present', function() { - it('should return the index where the element first appears in the array', function() { + context('when present', function () { + it('should return the index where the element first appears in the array', function () { [1, 2, 3].indexOf(3).should.equal(2); }); }); @@ -1532,13 +1536,13 @@ describe('Array', function() { The **TDD** interface provides `suite()`, `test()`, `suiteSetup()`, `suiteTeardown()`, `setup()`, and `teardown()`: ```js -suite('Array', function() { - setup(function() { +suite('Array', function () { + setup(function () { // ... }); - suite('#indexOf()', function() { - test('should return -1 when not present', function() { + suite('#indexOf()', function () { + test('should return -1 when not present', function () { assert.equal(-1, [1, 2, 3].indexOf(4)); }); }); @@ -1551,17 +1555,17 @@ The **Exports** interface is much like Mocha's predecessor [expresso][]. The key ```js module.exports = { - before: function() { + before: function () { // ... }, Array: { '#indexOf()': { - 'should return -1 when not present': function() { + 'should return -1 when not present': function () { [1, 2, 3].indexOf(4).should.equal(-1); - } - } - } + }, + }, + }, }; ``` @@ -1576,12 +1580,12 @@ function ok(expr, msg) { suite('Array'); -test('#length', function() { +test('#length', function () { var arr = [1, 2, 3]; ok(arr.length == 3); }); -test('#indexOf()', function() { +test('#indexOf()', function () { var arr = [1, 2, 3]; ok(arr.indexOf(1) == 0); ok(arr.indexOf(2) == 1); @@ -1590,7 +1594,7 @@ test('#indexOf()', function() { suite('String'); -test('#length', function() { +test('#length', function () { ok('foo'.length == 3); }); ``` @@ -1607,13 +1611,13 @@ var pre = require('mocha').before; var assertions = require('mocha').it; var assert = require('chai').assert; -testCase('Array', function() { - pre(function() { +testCase('Array', function () { + pre(function () { // ... }); - testCase('#indexOf()', function() { - assertions('should return -1 when not present', function() { + testCase('#indexOf()', function () { + assertions('should return -1 when not present', function () { assert.equal([1, 2, 3].indexOf(4), -1); }); }); @@ -1717,9 +1721,9 @@ The Doc reporter outputs a hierarchical HTML body representation of your tests. For example, suppose you have the following JavaScript: ```js -describe('Array', function() { - describe('#indexOf()', function() { - it('should return -1 when the value is not present', function() { +describe('Array', function () { + describe('#indexOf()', function () { + it('should return -1 when the value is not present', function () { [1, 2, 3].indexOf(5).should.equal(-1); [1, 2, 3].indexOf(0).should.equal(-1); }); @@ -1875,7 +1879,7 @@ mocha.setup('tdd'); // This is equivalent to the above. mocha.setup({ - ui: 'tdd' + ui: 'tdd', }); // Examples of options: @@ -1890,7 +1894,7 @@ mocha.setup({ retries: 3, slow: '100', timeout: '2000', - ui: 'bdd' + ui: 'bdd', }); ```