带有 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 单元测试 = 发送后无法设置标头的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义标头的 Express 正文解析器

如何为 chai expect 提供 mocha 单元测试的自定义错误消息?

Node Express Mocha 测试:TypeError: chai.request is not a function

如何使用 mongoose、mocha 和 chai 编写用于向 mongodb 插入和检索对象的单元测试?

如何对 express-graphql 突变进行单元测试?

续集测试 - 有时验证错误