带有 Chai 错误的 Express 单元测试 = 发送后无法设置标头
Posted
技术标签:
【中文标题】带有 Chai 错误的 Express 单元测试 = 发送后无法设置标头【英文标题】:Express unit Test with Chai Error=Cannot set headers after they are sent 【发布时间】:2019-08-03 05:45:49 【问题描述】:TLDR:我的 api 与 curl 和 postman 一起工作正常,但在一个 put 请求中使用 chai-http 时测试失败,并出现错误“无法设置 headers after they are sent to the client”(所有其他 put 请求都有效)。我的所有集合都有一个通用的更新方法(附在下面),它适用于其他更新/放置请求。感谢您的帮助。
客户系列的 Mocha & Chai 测试
process.env.NODE_ENV = 'test';
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const c_paths = require('../constants/paths.js');
const app = require('../app.js');
chai.use(chaiHttp);
describe(c_paths.clients, () =>
it('It should create a new clients document', done =>
let req =
test: true,
data: [
name: 'client 1',
contacts: [
name: 'client name 1',
mobile: 'client mobile 1',
email: 'client1@email.com',
,
],
address: ['client address 1'],
,
],
;
chai
.request(app)
.post(c_paths.clients)
.send(req)
.end((err, res) =>
expect(res).to.have.status(200);
expect(res.body.insertedCount).is.equal(
1,
'Number of inserted documents.',
);
expect(res.body.ops[0].name).is.equal(req.data[0].name);
expect(res.body.ops[0].contacts[0].name).is.equal(
req.data[0].contacts[0].name,
);
expect(res.body.ops[0].contacts[0].mobile).is.equal(
req.data[0].contacts[0].mobile,
);
expect(res.body.ops[0].contacts[0].email).is.equal(
req.data[0].contacts[0].email,
);
expect(res.body.ops[0].address[0]).is.equal(req.data[0].address[0]);
expect(res.body.ops[0].pause_updates).is.false;
expect(res.body.ops[0].pause_updates_toggle).is.false;
done();
);
);
it.('It should add a reccuring job', done =>
let req =
test: true,
filter:
name: 'client 1',
,
set:
recurring_jobs: [
job_id: 'job id 1',
current_employee_id: 'employee id 1',
employee_scheduled_till: 'Apr 30 2019',
notes: ['note 1'],
duties: ['duty 1'],
repeat_in_days: 7,
time: '11:00:00', // Validation error switched to 10000
start_date: 'Mar 01 2019',
end_date: 'May 30 2019',
is_active: true,
,
],
,
;
chai
.request(app)
.put(c_paths.clients)
.send(req)
.end((err, res) =>
expect(res).to.have.status(200);
expect(res.body.ok).is.equal(1);
done();
);
);
it('It should delete one document', done =>
let req =
test: true,
filter:
name: 'client 1',
,
;
chai
.request(app)
.delete(c_paths.clients)
.send(req)
.end((err, res) =>
expect(res).to.have.status(200);
expect(res.body.n).is.equal(1, 'Number of documents removed');
done();
);
);
);
./routes/crud.js 中的更新方法。第 131 行的错误是 res.status(200)发送(结果);
function update(req, res, dbName, collectionName, joiSchema)
var toValidate = '[';
var data = '';
if (req.body.set)
toValidate = toValidate.concat(JSON.stringify(req.body.set), ',');
data = data.concat('"$set":', JSON.stringify(req.body.set), ',');
if (req.body.inc)
toValidate = toValidate.concat(JSON.stringify(req.body.inc), ',');
data = data.concat('"$inc":', JSON.stringify(req.body.inc), ',');
if (req.body.addToSet)
let temp = JSON.stringify(req.body.addToSet);
temp = temp.replace('"$each":[', '[');
temp = temp.slice(0, -1); // Removes $each for validation
toValidate = toValidate.concat(temp, ',');
data = data.concat('"$addToSet":', JSON.stringify(req.body.addToSet), ',');
if (req.body.unset)
data = data.concat('"$unset":', JSON.stringify(req.body.unset), ',');
if (req.body.currentDate)
toValidate = toValidate.concat(JSON.stringify(req.body.currentDate), ',');
data = data.concat(
'"$currentDate":',
JSON.stringify(req.body.currentDate),
',',
);
if (req.body.rename)
data = data.concat('"$rename":', JSON.stringify(req.body.rename), ',');
if (req.body.pullAll)
toValidate = toValidate.concat(JSON.stringify(req.body.pullAll), ',');
data = data.concat('"$pullAll":', JSON.stringify(req.body.pullAll), ',');
toValidate = toValidate.slice(0, -1); // Removes trailing comma
data = data.slice(0, -1);
toValidate = toValidate.concat(']');
data = data.concat('');
Joi.validate(toValidate, joiSchema, function(err, value)
if (err)
res.send(err);
return;
);
MongoClient.connect(c_db.url, function(err, db)
if (err)
res.send(err);
db.close();
return;
const dbo = db.db(dbName);
const filter = req.body.filter == null ? '' : req.body.filter;
dbo
.collection(collectionName)
.updateMany(filter, JSON.parse(data), function(err, result)
if (err)
res.send(err);
db.close();
return;
res.status(200).send(result); //Error Line 131
db.close();
);
);
;
错误响应
错误 [ERR_HTTP_HEADERS_SENT]:发送后无法设置标头 给客户 在 ServerResponse.setHeader (_http_outgoing.js:482:11) 在 ServerResponse.header (./App/node_modules/express/lib/response.js:767:10) 在 ServerResponse.send (./App/node_modules/express/lib/response.js:170:12) 在 ServerResponse.json (./App/node_modules/express/lib/response.js:267:15) 在 ServerResponse.send (./App/node_modules/express/lib/response.js:158:21) 在 ./App/routes/crud.js:131:25 结果(./App/node_modules/mongodb/lib/utils.js:414:17) 在 session.endSession (./App/node_modules/mongodb/lib/utils.js:401:11) 在 ClientSession.endSession (./App/node_modules/mongodb-core/lib/sessions.js:129:41) 在 executeCallback (./App/node_modules/mongodb/lib/utils.js:397:17)
【问题讨论】:
当您连续两次触发请求时,您通常会看到诸如Cannot set headers after they are sent to the client
之类的错误。我建议您仔细查看期望,看看您是否正在这样做
您的调用堆栈正在执行send, json, send, head, setHeader
,这又回到了您的./App/routes/crud.js
的第131 行,那么这说明了什么?
@Keith,我已经编辑了问题以显示错误行,为方便起见,这里是 ./App/routes/crud.js 在 chai.request.end 的更新函数中的错误行调用 res.status(200).send(result);完毕();同样的方法也用于更新其他集合,并且这些测试通过没有问题。
【参考方案1】:
这是一个 Joi 验证错误,为了弄清楚我将 JSON.stringify(req.body) 添加到预期错误或 console.log 中,即 期望(res.body.n, JSON.stringify(req.body).is.equal(1);
这给了我摩卡测试中的验证错误,一旦修复它就可以正常工作。
【讨论】:
以上是关于带有 Chai 错误的 Express 单元测试 = 发送后无法设置标头的主要内容,如果未能解决你的问题,请参考以下文章
如何为 chai expect 提供 mocha 单元测试的自定义错误消息?
Node Express Mocha 测试:TypeError: chai.request is not a function