每次调用时,Sinon.stub()都会返回不同的值

这里是我正在写testing的代码:

'use strict'; var internals = {}; var _ = require('lodash'); module.exports = { initialize: function (query) { internals.query = query; }, createField: function (fieldId, accountId, payload) { function callQuery (parList) { var query = 'INSERT into fields VALUES (:uuid, :accountId, :shortcutName, :displayName, :fieldType, :widgetType, :columnOrder, :options, :required, NULL)'; return internals.query(query, parList, function () { return fieldId; }); } var increment = 10; var parameterList = { 'uuid': fieldId, 'accountId': accountId, 'shortcutName': payload.shortcutName, 'displayName': payload.displayName, 'fieldType': payload.fieldType, 'widgetType': payload.widgetType, 'columnOrder': payload.columnOrder, 'options': JSON.stringify(payload.options) || null, 'required': payload.required || 'f' }; if (!payload.columnOrder) { var columnQuery = 'SELECT MAX(column_order) from fields'; return internals.query(columnQuery, {}, function (x) {return x; }) .then(function (results) { var highestColumnOrder = results[0]['MAX(column_order)']; var newHighestColumnOrder = Math.ceil(highestColumnOrder / 10) * 10; if (newHighestColumnOrder > highestColumnOrder) { parameterList.columnOrder = newHighestColumnOrder; } else { parameterList.columnOrder = newHighestColumnOrder + increment; } return callQuery(parameterList); }); } else { return callQuery(parameterList); } }, getFieldsByAccountId: function(accountId, showDeleted) { var callQuery = function(paramList) { var query = 'SELECT ' + paramList.columns.join(", ") + ' FROM fields WHERE account_id = :account_id'; if (!showDeleted) { query += ' AND archived_at IS NULL'; } return internals.query(query, paramList, function(rows) { return _.each(rows, function(row) { if(row.options) { row.options = JSON.parse(row.options); } row.required = !!row.required; }); }); }; var columnList = ["uuid", "account_id", "shortcut_name", "display_name", "field_type", "required", "column_order", "options"]; var paramList = {'account_id': accountId}; if (showDeleted) { columnList.push("archived_at"); } _.extend(paramList, {'columns': columnList}); return callQuery(paramList); } }; 

这是我的testing:

  'use strict'; var assert = require('assert'); var sinon = require('sinon'); var Promise = require('bluebird'); var proxyquire = require('proxyquire'); var returnedValues = require('../../../return_values.js'); var fieldGateway = proxyquire('../../../../src/fields/lib/gateway', {}); describe('gateway', function () { var accountId = 100; var fieldId = 200; var _query, sql, mockData, rows; describe('createField', function() { describe('is successful with a column order value', function () { beforeEach(function() { sql = 'INSERT into fields VALUES (:uuid, :accountId, :shortcutName, :displayName, :fieldType, :widgetType, :columnOrder, :options, :required, NULL)'; mockData = returnedValues.getFieldInputValues(); }); it("should only insert new field", function () { _query = sinon.spy(function() { return Promise.resolve(); }); fieldGateway.initialize(_query); fieldGateway.createField(fieldId, accountId, mockData); mockData.accountId = accountId; mockData.uuid = fieldId; mockData.options = JSON.stringify(mockData.options); assert.equal(sql, _query.getCall(0).args[0]); assert.deepEqual(mockData, _query.getCall(0).args[1]); }); it.only("_query should be called with the right sql statement and parameterList", function () { _query = sinon.stub().returns(Promise.resolve(fieldId)); // _query.onCall(0).returns(Promise.resolve([{'MAX(column_order)': 10}])); // _query.onCall(1).returns(Promise.resolve(fieldId)); fieldGateway.initialize(_query); delete mockData.columnOrder; fieldGateway.createField(fieldId, accountId, mockData); console.log(_query.args); assert.equal(sql, _query.getCall(0).args[0]); fieldGateway.createField.restore(); }); }); }); }); 

问题是当testing运行时,唯一运行的SQL查询是SELECT语句。 应该发生的是一个SQL语句运行,然后运行一个INSERT语句

发生这种情况是因为bluebird是一个真正的Promise / A +兼容库。 根据定义,所有链接的承诺必须在不同的执行时间内运行。 所以只有第一个promise是同步执行的(在同一个tick中)。

你应该告诉摩卡“等”剩下的行为。 你可以通过在你的unit testing中指定一个donecallback来做到这一点,并当你的承诺完成他们的工作时相应地调用它

  it.only("_query should be called with the right sql statement and parameterList", function (done) { _query = sinon.stub().returns(Promise.resolve(fieldId)); fieldGateway.initialize(_query); delete mockData.columnOrder; fieldGateway.createField(fieldId, accountId, mockData) .then(function(){ /// assertion code should be adjusted here console.log(_query.args); assert.equal(sql, _query.getCall(0).args[0]); fieldGateway.createField.restore(); //tell Mocha we're done, it can stop waiting done(); }) .catch(function(error) { //in case promise chain was rejected unexpectedly //gracefully fail the test done(error); }; }); 

每当你testing你的承诺返回函数,你应该总是处理结果