msw: Server does not complete setup before next test suite executes in Mocha
Environment
| Name | Version |
|---|---|
| msw | 0.27.0 |
| node | 12.19.0 |
| OS | Linux x86_64 |
Request handlers
Note: I have created a repo displaying the behavior here.
App.test.js
import React from 'react';
import { render } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import App from '../App';
describe('App tests', () => {
const server = setupServer(
rest.post('http://localhost:8080/api/getList', (req, res, ctx) => {
return res(
ctx.set('access-control-allow-origin', '*'),
ctx.json({
msgCode: 0,
msgDesc: 'Request processed successfully',
data: {
data: {
createdBy: 'CJ',
createdDate: '02/18/2021',
},
pagination: {
pageNo: req.body.pagination.pageNo,
pageSize: req.body.pagination.pageSize,
total: 1,
sortedColumn: req.body.pagination.sortedColumn,
sortedType: req.body.pagination.sortedType,
},
},
})
);
})
);
before(() => {
server.listen();
});
after(() => {
server.close();
});
it('renders App without crashing', () => {
render(<App />);
});
});
Child.test.js
import React from 'react';
import { render } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import Child from '../Child';
describe('Child tests', () => {
const server = setupServer(
rest.post('http://localhost:8080/api/getChildList', (req, res, ctx) => {
return res(
ctx.set('access-control-allow-origin', '*'),
ctx.json({
msgCode: 0,
msgDesc: 'Request processed successfully',
data: {
data: {
createdBy: 'CJ Child',
createdDate: '02/18/2021',
},
pagination: {
pageNo: req.body.pagination.pageNo,
pageSize: req.body.pagination.pageSize,
total: 1,
sortedColumn: req.body.pagination.sortedColumn,
sortedType: req.body.pagination.sortedType,
},
},
})
);
})
);
before(() => {
server.listen();
});
after(() => {
server.close();
});
it('renders Child without crashing', () => {
render(<Child />);
});
});
Actual request
The actual request is simply performed on mount via the Fetch API (in our tests we are utilizing the isomorphic-fetch library to polyfill Fetch for Node).
function Child() {
useEffect(() => {
fetch('http://localhost:8080/api/getChildList', {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pagination: {
pageSize: 10,
pageNo: 0,
sortedColumn: 'createdBy',
sortedType: 'asc',
},
}),
})
.then(async (response) => {
console.log('Fetch complete: ', await response.json());
})
.catch((error) => {
console.error('Failed to fetch: ', error);
});
}, []);
return (
<React.Fragment>
<h1>Child</h1>
</React.Fragment>
);
}
Current behavior
If we are running two test suites consecutively, in this case, App.test.js and then Child.test.js in quick succession, the second server is not fully set up before the test executes, resulting in something like this:
> mocha-ref@1.0.0 test:watch
> cross-env NODE_ENV=test mocha --timeout 10000 -w src/**/*.test.js
App tests
✓ renders App without crashing
Fetch complete: {
msgCode: 0,
msgDesc: 'Request processed successfully',
data: {
data: { createdBy: 'CJ', createdDate: '02/18/2021' },
pagination: {
pageNo: 0,
pageSize: 10,
total: 1,
sortedColumn: 'createdBy',
sortedType: 'asc'
}
}
}
Child tests
✓ renders Child without crashing
Failed to fetch: FetchError: request to http://localhost:8080/api/getChildList failed, reason: connect ECONNREFUSED 127.0.0.1:8080
at ClientRequest.<anonymous> (/home/cjones26/JS/mocha-ref/node_modules/node-fetch/lib/index.js:1461:11)
at ClientRequest.emit (events.js:314:20)
at Socket.socketErrorListener (_http_client.js:428:9)
at Socket.emit (events.js:314:20)
at emitErrorNT (internal/streams/destroy.js:92:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
type: 'system',
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED'
}
2 passing (59ms)
ℹ [mocha] waiting for changes...
Expected behavior
> mocha-ref@1.0.0 test:watch
> cross-env NODE_ENV=test mocha --timeout 10000 -w src/**/*.test.js
App tests
✓ renders App without crashing
Fetch complete: {
msgCode: 0,
msgDesc: 'Request processed successfully',
data: {
data: { createdBy: 'CJ', createdDate: '02/18/2021' },
pagination: {
pageNo: 0,
pageSize: 10,
total: 1,
sortedColumn: 'createdBy',
sortedType: 'asc'
}
}
}
Child tests
✓ renders Child without crashing
Fetch complete: {
msgCode: 0,
msgDesc: 'Request processed successfully',
data: {
data: { createdBy: 'CJ Child', createdDate: '02/18/2021' },
pagination: {
pageNo: 0,
pageSize: 10,
total: 1,
sortedColumn: 'createdBy',
sortedType: 'asc'
}
}
}
2 passing (59ms)
ℹ [mocha] waiting for changes...
Screenshots
Failed test:

About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 1
- Comments: 16 (9 by maintainers)
@cjones26 The magic here is the usage of the
--fileflag in the CLI (or inmocharcin this case). The server creation/cleanups are always rerun and skip the awkward handling of the lifecycle hooks behavior from mocha.This behavior is slightly different than what I did originally where I created the server once, then set it to global.
Either works, but this is the best we can do for now. We’re going to continue to research supporting parallel mode - if we resolve that, I’ll let you know so you can update if necessary. Thanks again for providing this issue!
@kettanaito You might be right, but I’m not 100% sure and it’s kind of a pain to debug. By default, mocha runs sequentially and you have to opt-in to
parallel=true. This seems to be more of a problem with how mocha is running tests, but I’m no mocha expert.For example, in the repo as-is, if you remove the
server.close()from each test file, it’ll run them all just fine but would lead to cleanup issues.As an immediate solution that works with both sequential and parallel runs, what I’d recommend @cjones26 does is change the mocha configuration with these steps:
In the given repro, you’d add this file:
Update mocha config to include the new hooks file:
Refactor tests to look like:
and
As general guidance, I’d move the request handlers and server instance creation out into their own files so you can more easily reuse them in other places such as Storybook.
@marcosvega91 Thanks again! I merged that into my repo and into the PR for @cjones26. I think this is as good as we’re going to get for right now. We will have to investigate supporting parallel mode, which may or may not have to do with #474 as @kettanaito said 😃
@msutkowski – sure thing – as of now I am sure we are fine and won’t need parallel mode. Thanks again, closing this out.
@msutkowski I have looked into the code and update it a little bit using the before and after function in a global scope. I have add my code here. Let me know what you think
@msutkowski – you beat me to responding but yes it does appear to be something with the way that Mocha is working internally, and agree that it would be a pain to debug. I have not looked into the implementation of the functions, but my thought was that it could be helpful if
server.listenandserver.closeboth returned a promise which resolved once the server was fully started and/or fully cleaned up.As for your suggestion above, I believe this will work for our use case and I will refactor our real project (not the provided sample) in order to validate this.
Thanks!