supertest
supertest copied to clipboard
Request works with when I do server.listen() myself, but not when I pass Express app directly to supertest.
I have a server with a "/hang" endpoint. Requests will hang until the test calls the "release()" function to complete the request. My test initiates two requests to "/hang", then calls "release()" and await
s their completion.
My problem is that the await
doesn't ever return. I noticed two changes I can make to cause it to return:
- Call
expressApp.listen(...)
myself instead of doingsupertest.agent(expressApp)
. See theCALL_LISTEN_OURSELVES
flag. - Call
release()
on both requests before callingawait
. See theRELEASE_ALL_THEN_AWAIT
flag.
I don't understand why those two things should make a difference.
const express = require('express');
const supertest = require('supertest');
async function mainAsync() {
const expressApp = express();
// Server definition.
const resById = new Map();
expressApp.get('/hang', (req, res) => {
const id = req.query.id;
if (id === undefined) {
res.status(400).send('Missing "id" parameter.');
return;
}
console.log(`server: GET /hang, id=${JSON.stringify(id)}`);
req.on('end', () => {
console.log(`server: GET /hang, id=${JSON.stringify(id)}, 'end' event`);
});
resById.set(id, res);
});
const release = (id) => {
const res = resById.get(id);
if (res === undefined) {
throw u.spawnError(`unknown id: ${JSON.stringify(id)}`);
}
res.status(200).send('released');
};
// Create supertest agent.
let agent;
const CALL_LISTEN_OURSELVES = false;
if (CALL_LISTEN_OURSELVES) {
let server;
await new Promise(resolve => {
server = expressApp.listen(0, resolve);
});
agent = supertest.agent(`http://127.0.0.1:${server.address().port}`);
} else {
agent = supertest.agent(expressApp);
}
// Start two "/hang" requests
const requests = [];
for (let i = 0; i < 2; i++) {
const id = `${i}`;
console.log(`client: GET /hang, id=${JSON.stringify(id)}`);
requests.push(agent.get(`/hang?id=${i}`).then(r => r)); // The '.then' is to force the request to start.
}
// Wait until both "/hang" Response objects are in 'resById'...
console.log(`client: waiting until resById.size === ${requests.length}`);
while (resById.size !== requests.length) {
await setTimeoutAsync(10);
}
console.log(`client: resById.size === ${requests.length}`);
// Call release() to end the hanging requests.
const RELEASE_ALL_THEN_AWAIT = false;
if (RELEASE_ALL_THEN_AWAIT) {
for (let i = 0; i < requests.length; i++) {
const id = `${i}`;
console.log(`client: release(${JSON.stringify(id)})`);
release(id);
}
for (let i = 0; i < requests.length; i++) {
const id = `${i}`;
console.log(`client: awaiting ${JSON.stringify(id)}...`);
const r = await requests[i];
console.log(`client: got response: ${JSON.stringify(r.body)}`);
}
} else {
for (let i = 0; i < requests.length; i++) {
const id = `${i}`;
console.log(`client: release(${JSON.stringify(id)})`);
release(id);
console.log(`client: awaiting ${JSON.stringify(id)}...`);
const r = await requests[i];
console.log(`client: got response: ${JSON.stringify(r.body)}`);
}
}
console.log('client: done');
}
function setTimeoutAsync(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
if (require.main === module) {
mainAsync().catch(err => console.log(err));
}
package.json
{
"name": "supertest-hang-release",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.17.1",
"supertest": "4.0.2"
}
}