我对javascript中的Promises非常陌生,所以这个问题是为了帮助我弄清楚为什么我使用promise会收到错误(以奇怪的顺序)。最重要的是,我正忙着第一次使用ms-sql repo,sinonjs和restify,所以这并没有帮助
这个问题与我之前提到的here
有关在上面的问题中,我需要在@robertklep的帮助下删除我成功完成的SQL DB。但是,作为一个完整性检查,我想检查端点是否仍然像以前一样返回我期望的数据,所以我解除了存根。现在我收到以下错误,我不明白为什么:
[错误:发送后无法设置标头。]
测试:
<app-location>
终点:
'use strict';
var expect = require('chai').expect,
request = require('supertest'),
chance = require('chance').Chance(),
server = require('../server'),
sinon = require('sinon'),
select = require('../../helpers/data_access/select'),
read_file = require('../../helpers/read_file');
describe("/account_types", function () {
// before(function (done) {
// sinon
// .stub(select, "query_list")
// .returns([{id: "test"}]);
//
// sinon
// .stub(select, "query_single")
// .returns({id: "test"});
//
// sinon
// .stub(read_file, "get_file_contents")
// .returns("Some content to get excited about");
//
// done();
// });
//
// after(function (done) {
// select
// .query_list
// .restore();
// select
// .query_single
// .restore();
// read_file
// .get_file_contents
// .restore();
//
// done();
// });
it('GET 200 List', function (done) {
request(server.baseURL)
.get('/api/v1/account_types')
.set('Accept', 'application/json')
.expect('Content-Type', 'application/json')
.expect(200)
.end(function (err, res) {
/* istanbul ignore if */
if (err)
return done(err);
expect(res.body).to.not.be.null;
expect(res.body).to.not.be.undefined;
expect(res.body).to.be.an('Array');
expect(res.body.length).to.be.above(0);
//expect(select.query_list).to.have.been.calledOnce;
return done();
});
});
it('GET 200 Single', function (done) {
//var param = chance.random();
request(server.baseURL)
.get('/api/v1/account_type/' + 1)
.set('Accept', 'application/json')
.expect('Content-Type', 'application/json')
.expect(200)
.end(function (err, res) {
/* istanbul ignore if */
if (err)
return done(err);
expect(res.body).to.not.be.null;
expect(res.body).to.not.be.undefined;
expect(res.body).to.be.an('Object');
expect(res.body.toString().length).to.be.above(0);
return done();
});
});
it('POST 200 Single', function (done) {
var param = chance.random();
request(server.baseURL)
.post('/api/v1/account_type')
.set('Accept', 'application/json')
.send({id: param})
.expect('Content-Type', 'application/json')
.expect(200)
.end(function (err, res) {
/* istanbul ignore if */
if (err)
return done(err);
expect(res.body).to.be.an('object');
expect(res.body).to.include.keys('result');
expect(res.body.result).to.equal('post account_type : ' + param.toString());
return done();
});
});
it('PUT 200 Single', function (done) {
var param = chance.random();
request(server.baseURL)
.put('/api/v1/account_type')
.set('Accept', 'application/json')
.send({id: param})
.expect('Content-Type', 'application/json')
.expect(200)
.end(function (err, res) {
/* istanbul ignore if */
if (err)
return done(err);
expect(res.body).to.be.an('object');
expect(res.body).to.include.keys('result');
expect(res.body.result).to.equal('put account_type : ' + param.toString());
return done();
});
});
it('DELETE 200 Single', function (done) {
var param = chance.random();
request(server.baseURL)
.delete('/api/v1/account_type/' + param)
.set('Accept', 'application/json')
.expect('Content-Type', 'application/json')
.expect(200)
.end(function (err, res) {
/* istanbul ignore if */
if (err)
return done(err);
expect(res.body).to.be.an('object');
expect(res.body).to.include.keys('result');
expect(res.body.result).to.equal('delete account_type : ' + param.toString());
return done();
});
});
});
select.js:
var select = require('../helpers/data_access/select'),
read_file = require('../helpers/read_file'),
format = require('string-format');
const db_config_name = 'db.goaml';
module.exports = function (server) {
server.get('/api/v1/account_types', function (req, res, next) {
var query = read_file.get_file_contents('path to query');
select.query_list(db_config_name, query, function (err, records) {
console.log('test 1');
if (err != null) {
return next(err);
}
res.send(records);
//return next(); <-- EDIT: Removed as per Mike Perrenoud answer
});
//return next(); <-- EDIT: Removed as per Mike Perrenoud answer
});
server.get('/api/v1/account_type/:id', function (req, res, next) {
var query =
format(read_file.get_file_contents('path to query'), req.params.id);
select.query_single(db_config_name, query, function (err, records) {
console.log('test 2');
if (err != null) {
return next(err);
}
res.send(records[0]);
//return next(); <-- EDIT: Removed as per Mike Perrenoud answer
});
//return next(); <-- EDIT: Removed as per Mike Perrenoud answer
});
server.post('/api/v1/account_type', function (req, res, next) {
res.send({'result': 'post account_type : ' + req.body.id});
return next();
});
server.put('/api/v1/account_type', function (req, res, next) {
res.send({'result': 'put account_type : ' + req.body.id});
return next();
});
server.del('/api/v1/account_type/:id', function (req, res, next) {
res.send({'result': 'delete account_type : ' + req.params.id});
return next();
});
};
当我运行gulp任务来测试上面提到的代码时,我得到以下输出:
var sql = require('mssql'),
config = require('./configs/config');
module.exports = {
query_list: function (config_name, sql_query, callback) {
return query(config_name, sql_query, true, callback);
},
query_single: function (config_name, sql_query, callback) {
return query(config_name, sql_query, false, callback);
}
};
function query(config_name, sql_query, isList, callback) {
var db_config = config.get(config_name),
connection = new sql.Connection(db_config);
connection.connect(function () {
new sql.Request(connection)
.query(sql_query)
.then(function (records) {
console.log('test 3');
callback(null, isList ? records : records[0]);
connection.close();
})
.catch(function (err) {
console.log('test 4');
console.log(err);
callback(err, null);
});
});
// EDIT for answer:
// This catch is not allowed. IE the connection isn't a promise. Thus
// when the code responded with something valid, it reached this part
// after that and realised this is an illegal statement. As a result
// it throws an error after the res.send was already hit and showing the
// initial error I reported.
// .catch(function (err) {
// console.log('test 5');
// callback(err, null);
// });
}
令我感到困惑的是,如果我查看C:\Code\JS\general_admin_service>gulp test
[16:12:47] Using gulpfile C:\Code\JS\general_admin_service\gulpfile.js
[16:12:47] Starting 'test'...
[16:12:47] Finished 'test' after 62 ms
/account_types
1) GET 200 List
2) GET 200 Single
test 3
test 1
test 4
[Error: Can't set headers after they are sent.]
test 1
√ POST 200 Single
test 3
test 2
test 4
[Error: Can't remove headers after they are sent.]
test 2
√ PUT 200 Single
√ DELETE 200 Single
3 passing (400ms)
2 failing
1) /account_types GET 200 List:
Error: expected 200 "OK", got 500 "Internal Server Error"
at Test._assertStatus (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:232:12)
at Test._assertFunction (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:247:11)
at Test.assert (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:148:18)
at assert (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:127:12)
at C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:124:5
at Test.Request.callback (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\index.js:831:3)
at Stream.<anonymous> (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\index.js:1049:12)
at Unzip.<anonymous> (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\utils.js:108:12)
2) /account_types GET 200 Single:
Error: expected 200 "OK", got 500 "Internal Server Error"
at Test._assertStatus (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:232:12)
at Test._assertFunction (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:247:11)
at Test.assert (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:148:18)
at assert (C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:127:12)
at C:\Code\JS\general_admin_service\node_modules\supertest\lib\test.js:124:5
at Test.Request.callback (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\index.js:831:3)
at Stream.<anonymous> (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\index.js:1049:12)
at Unzip.<anonymous> (C:\Code\JS\general_admin_service\node_modules\superagent\lib\node\utils.js:108:12)
events.js:142
throw er; // Unhandled 'error' event
^
Error: 2 tests failed.
的输出,很明显代码会进入&#34;成功&#34; console.log's
的{{1}}块并调用回调。我可以确认,如果我添加一个then
来检查记录参数的值,实际上会有数据返回。
此时sql.request
获得了点击,但是console.log
的{{1}}块中出现了错误,此时此处出现了混淆:
如果成功返回,为什么捕获会被击中?
代码的哪一部分正在响应测试。 I.E.为什么标题在设置后会被更改? (res.send
可能与它有关吗?)
如前所述,这个问题与我之前的问题有很大关系。我不得不对代码进行更改以满足我的单元测试。但是,我认为有必要进行健全性检查并确保数据仍然按预期返回,此时,我认为ms-sql使用的Promise框架只会将值返回到测试中。 (我并不反对Promise框架的价值)。
我希望有人知道我做错了什么。
编辑:
所以我对发生的事情感到有些困惑。使用代码,如果我取消注释测试的catch
和sql.query
部分,测试将失败,看起来它与回调有关。
如果我完全删除回调并在next(err)
的{{1}}和before
after
中返回,那么我的测试就会通过。
但是,如果我直接测试没有存根,此设置将不会返回值。
答案 0 :(得分:2)
这种情况正在发生,因为您正在执行return next();
之前来自SQL Server的回调已完成。问题的原因是,虽然它本质上是异步的,但你整体上都在执行一组同步操作。
发生了什么res.send(records);
失败,因为next()
已被调用且服务器已返回200
。
最后,return next();
之后您不需要res.send(records);
因为res.send(records);
结束了流程并返回了响应。