nodejs mssql并承诺混淆

时间:2016-06-06 14:51:48

标签: sql-server node.js promise sinon

我对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框架的价值)。

我希望有人知道我做错了什么。

编辑:

所以我对发生的事情感到有些困惑。使用代码,如果我取消注释测试的catchsql.query部分,测试将失败,看起来它与回调有关。

如果我完全删除回调并在next(err)的{​​{1}}和before after中返回,那么我的测试就会通过。

但是,如果我直接测试没有存根,此设置将不会返回值。

1 个答案:

答案 0 :(得分:2)

这种情况正在发生,因为您正在执行return next(); 之前来自SQL Server的回调已完成。问题的原因是,虽然它本质上是异步的,但你整体上都在执行一组同步操作。

发生了什么res.send(records);失败,因为next()已被调用且服务器已返回200

最后,return next();之后您不需要res.send(records);因为res.send(records);结束了流程并返回了响应。