2015-07-14 2 views
1

Вот код, который я пишу тесты для:Sinon.stub() возвращать разные значения каждый раз, когда это называется

'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); 
    } 
}; 

Вот мой тест:

 '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(); 
      }); 
     }); 
    }); 

}); 

Проблема заключается в том, что, когда тестовые прогоны, единственным запросом SQL является оператор SELECT. Что должно произойти, выполняется один оператор SQL, затем выполняется инструкция INSERT

+0

ли первый кодовый блок должен быть частью другой функции? Второй оператор 'return' не принадлежит функции. – kevin628

+0

И я предполагаю, что объект 'mockData' в вашем тесте находится где-то в вашем' beforeEach' или таком? – kevin628

+0

@ kevin628 обновленный тест, чтобы включить весь тестовый файл – dennismonsewicz

ответ

2

Это происходит потому, что bluebird - это настоящая совместимая с Promise/A + библиотека. И по определению все прикованные обещания должны выполняться в другом типе исполнения. Поэтому только первое обещание выполняется синхронно (в том же тике).

Вы должны сказать мокко, чтобы «подождать», чтобы остальные действовали. Вы можете сделать это, указав done обратного вызова в тестовом модуле и назвав его соответствующим образом, когда ваши обещания закончили свою работу

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); 
    }; 
}); 

Всякий раз, когда вы проверяете обещание, возвращающие функции, которые вы всегда должны обрабатывать результат в .then

+0

Спасибо! Мне пришлось переместить 'done();' вызов за пределами 'then', но он работает !!! Я потратил, буквально, целый день, пытаясь понять это. Спасибо!!! – dennismonsewicz

+0

хм. Вы уверены, что одно из обещаний не разрешено? Если вы вызываете 'done()' _after_ вызов функции возврата-обещания, то он выполняется до того, как цепочка будет выполнена полностью. Возможно, в вашем случае вы должны добавить '.catch (function (error) {done (error);}', который будет ошибочно проверен, когда цепочка обещаний будет отклонена. Но оставление 'done()' вне проверяемой цепочки обещаний является логической ошибкой –

+0

Nevermind, у меня была синтаксическая ошибка: она работает внутри оператора 'then'! – dennismonsewicz