如何通过Mocha的模拟承诺进行测试?

时间:2015-05-17 00:47:04

标签: node.js unit-testing mocking promise mocha

我的node.js代码是:

function getPatientNotificationNumbers(patientId) {
    patientId = patientId && patientId.toString();

    var sql = "SELECT * FROM [notification_phone_number] ";
    sql += "WHERE patient_id = " + escapeSql(patientId);
    return sqlServer.query(sql).then(function(results) {
        var phoneNumbers = _.map(results[0], function (result) {
            var obj = {
                id: result.id,
                phoneNumber: result.phone_number,
                isPrimary: result.is_primary,
                isVerified: result.is_verified,
                patientId: result.patient_id
            }
            return obj;
        });

        return phoneNumbers;
    });
}

非常简单直接。我想测试的是,正确解析的这个函数的返回是一个匹配该格式的phoneNumbers数组。

sqlServerrequire以上,我在这个文件中有很多事情require' d。要将它们删除,我使用的是mockery,这似乎非常棒。

到目前为止,这是我的测试:

before(function() {
    deferred = Q.defer();
    mockery.enable();
    moduleConfig.init();
    mockery.registerSubstitute('moment', moment);
    mockery.registerAllowable('../../util');

    mockStubs.sqlServer = {
        query: sinon.stub().returns(deferred.promise)
    }

    mockery.registerMock('../../db/sqlserver', mockStubs.sqlServer);

    methods = require('../../../rpc/patient/methods');
});

beforeEach(function() {
    deferred = Q.defer();
})

it('should get the patient notification numbers', function(done) {
    // sinon.spy(sqlServer, 'query').and.returnValue(deferred.promise);
    deferred.resolve('here we go');
    methods.getPatientNotificationNumbers(1).then(function(result) {
        console.log(result);
        done();
    });


});

但是,我的代码中永远不会超过sqlServer.query。所以results毫无意义。我也尝试过这样的事情:

response = methods.getPatientNotificationNumbers(1)

但是当我console.log(response)时,它基本上是{state: 'pending'},我猜这是一个未解决的承诺。

所以我到处都是,并且我愿意使用任何图书馆让事情变得简单。我没有和mockerysinon或其他任何人结婚。任何建议都会有所帮助。

谢谢!

3 个答案:

答案 0 :(得分:4)

另一种方法是使用rewirederide的组合。

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div class="ng-scope" ng-app="myApp">
  <div ng-controller="dates">
    <ng-select-day-month ng-model-day="ngSelectDayMonth.day"></ng-select-day-month>
  </div>
</div>

这意味着您无需更改生产代码,您可以使用以下内容轻松开始测试不快乐的路径:

var should = require('should-promised');                                                                                                                                                        
var rewire = require('rewire');                                                                                                                                                                
var Somefile = rewire('./path/to/file');                                                                                                                                                       
var deride = require('deride');                                                                                                                                                                

var sut, mockSql;                                                                                                                                                                              
before(function() {                                                                                                                                                                            
  mockSql = deride.stub(['query']);                                                                                                                                                            
  Somefile.__set__('sqlServer', mockSql);                                                                                                                                                      
  sut = new Somefile();                                                                                                                                                                        
});                                                                                                                                                                                            

describe('getting patient notification numbers', function() {                                                                                                                                  
  beforeEach(function() {                                                                                                                                                                      
    mockSql.setup.query.toResolveWith(fakeSqlResponse);                                                                                                                                        
  });                                                                                                                                                                                          

  it('resolves the promise', function() {
    sut.getPatientNotificationNumbers(id).should.be.fulfilledWith(expectedPhoneNumbers);
  });

  it('queries Sql', function(done) {
    sut.getPatientNotificationNumbers(id).then(function() {
      mockSql.expect.query.called.once();
      done();
    });
  });
}); 

或者更简洁:

it('handles Sql errors', function(done) {
  mockSql.setup.query.toRejectWith(new Error('Booom'));
  sut.getPatientNotificationNumbers(id)
    .then(function(){
      done('should not have resolved');
    })
    .catch(function(e) {
       e.should.be.an.Error;
       e.message.should.eql('Booom');
       done();
    });
});

答案 1 :(得分:2)

我会稍微缩小并考虑测试策略。假设目标是在sql server上测试模型层(方法)。它可以在不删除sql server的情况下完成。您的测试套件层可以有一组util方法来创建,初始化和删除可以从before,beforeEach等调用的db。 这样做的优点:

  1. 测试实际产品代码路径更好(比存根代码路径更好)。
  2. 如果您怀疑底层图层会产生噪音,那么最好使用Stubbing。 sqlserver层可能很稳定。
  3. 模型层看起来很简单,不需要单独进行测试。
  4. 如果您尝试在模型层中测试sqlserver失败处理,那么存根是有意义的。然后,存根层可以伪造这样的错误 - 在模型代码中运行错误路径。
  5. 这是基于您的问题的有限视图。如果还有更多。 Pl做分享,我们可以从那里拿走它。

答案 2 :(得分:1)

在我看来,您的目的是测试传递.then()方法的函数,而不是sqlserver库,也不会返回它返回的实际承诺。我们可以假设这些已经过测试。

如果是这种情况,您可以简单地分解出该功能(从SQL结果中提取电话号码的功能)并单独测试。

您的代码将成为以下内容:

var getNumbers = require('./getNumbers');
function getPatientNotificationNumbers(patientId) {
    patientId = patientId && patientId.toString();

    var sql = "SELECT * FROM [notification_phone_number] ";
    sql += "WHERE patient_id = " + escapeSql(patientId);
    return sqlServer.query(sql).then(getNumbers);
}

...并且您的./getNumbers.js文件看起来像:

function getNumbers(results) {
    var phoneNumbers = _.map(results[0], function (result) {
        var obj = {
            id: result.id,
            phoneNumber: result.phone_number,
            isPrimary: result.is_primary,
            isVerified: result.is_verified,
            patientId: result.patient_id
        }
        return obj;
    });

    return phoneNumbers;
}

module.exports = getNumbers;

现在您可以单独测试它:

var getNumbers = require('../path/to/getNumbers');

...

it('should get the patient notification numbers', function(done) {
    var sampleResults = [ ... ]; // sample results from sqlServer
    var phoneNumbers = getNumbers(sampleResults);
    assert(...); // make what ever assertions you want
});