2017-04-10 50 views
1

我试图在AWS Firehose对象上模拟putRecord方法,但嘲笑不成功。代码最终调用aws-sdk api与firehose对象进行交谈,并与live aws服务进行交谈。下面的代码有什么错误?什么需要改变,以避免这个现场服务电话,并使模拟有效?嘲笑putRecord on firehose不能与aws-sdk-mock一起工作

有没有办法发送一个可引用对象,而不仅仅是普通对象,就像它在下面的回调一样?也就是说,在测试代码中使用类似callbackFunc的东西呢?

最终我还需要检查模拟是否被调用。我将如何实现这一目标?我能否以某种方式使用sinon.stub来实现这一点,以便以后可以验证?怎么样?

这里是代码和测试代码部分...修改为简单的形式发布。

作为文件一部分的代码说samplelogging.js。 ...

/*jshint strict:true */ 
/*jshint node:true */ 
/*jshint esversion:6 */ 
/*jshint mocha:true */ 
"use strict"; 

var Promise = require('bluebird'); 
var AWS = require('aws-sdk'); 
var uuid = require('uuid'); 

AWS.config.setPromisesDependency(Promise); 

var Logger = { 
    /** 
    * Get's a AWS Firehose Instance 
    */ 
    getFirehoseInstance: function getFirehoseInstance() { 
     if (!(this.firehose)) { 
      this.firehose = new AWS.Firehose({apiVersion: "2015-08-04", region: "us-west-2"}); 
     } 
     return this.firehose; 
    }, 

    getLogger: function getLogger(options) { 
     options = options || {}; 
     let self = this; 

     self.class = options.class; 
     self.firehose = self.getFirehoseInstance(); 

     return self; 
    }, 


    logInfo: function logInfo(dataToLog, callback) { 
     this._log("info", dataToLog)   
      .then(function (data){ 
       if (callback) { 
        callback(); 
       }     
      }); 
     return; 
    }, 

    /** 
    * @api: private 
    */ 
    _log: function _log(traceLevel, dataToLog) { 

     return new Promise(function(resolve, reject) { 
      var params = params || {}; 

      AWS.config.update({ logger: process.stdout }); 
      AWS.config.update({ retries: 3 }); 
      var recordParams = { 
       type: params.type || 'LogEntry' 
      }; 

      if (typeof dataToLog === 'string' || dataToLog instanceof String) { 
       recordParams.data = { message: dataToLog }; 
      } else { 
       recordParams.data = dataToLog; 
      } 

      recordParams.data.id = uuid.v1(); 
      recordParams.data.preciseTimestamp = Math.floor(new Date().getTime()/1000); 
      recordParams.data.class = this.class; 
      recordParams.data.traceLevel = traceLevel; 

      var firehoseRecordParams = { 
       DeliveryStreamName: "mystreamname", //replace mystreamname with real stream name 
       Record: { 
        Data: JSON.stringify(recordParams)+',\n' 
       } 
      }; 

      this.firehose.putRecord(firehoseRecordParams, function(err, recordId) { 
       console.log("debug: recordId returned by putRecord = " + JSON.stringify(recordId)); 
       return resolve(recordId); 
      }); 

     }.bind(this)); 
    } 

}; 

module.exports = Logger; 

这里是我的测试代码是文件的一部分,说sampleloggingtest.js ...

var expect = require('chai').expect; 
var Promise = require("bluebird"); 
var sinon = require("sinon"); 
var AWSMock = require('aws-sdk-mock'); 

describe.only("Logging tests", function() { 

     it.only("Test AWS firehose API invoked", function (done) { 

      let mylogger = Logger.getLogger({class: "Unit Test"}); 
      let firehoseInstance = mylogger.getFirehoseInstance(); 

      // want to have a callback function that returns a thenable object and not just an object. Not sure how to use it though with mock 
      // so for now this is just code that shows what i intend to do. 
      let callBackFunc = function(err, recordId) { 
        console.log("debug: returend from putRecord, recordId = " + JSON.stringify(recordId)); 
        return Promise.resolve(recordId); 
       }; 

      // calling mock as per the documentation at https://github.com/dwyl/aws-sdk-mock 
      AWSMock.mock('Firehose', 'putRecord', function(params, callback) { 
       console.log("debug: callback to putRecord to be called");     
       callback(null, {"RecordId": "12345"}); 
      }); 

      // calling a method that should call firehose logging but our mock should intercept it - though it doesn't. 
      mylogger.logInfo({ prop1: "value1" }, function(){ 
       console.log("debug: in the callback that was passed to logInfo..."); 
       done(); 
      }); 

     }); 
}); 
+0

我试图做同样的事情(模拟Firehose)并看到相同的结果(模拟不叫,只有真正的服务)。在aws-sdk-mock文档中,他们的测试代码使用var AWS = require('aws-sdk-mock'),而不是AWSMock。我不知道这是如何工作的,但如果它通过替换变量来起作用,那么名称可能很重要。 – mmorrisson

+0

所以我并不孤单。 :-)希望听到有人工作,或希望听到AWS开发人员... 顺便说一下,变量的名称不应该改变结果。我相信你试过AWS和它没有工作。 – mi10

回答

0

分享,我想通了答案,特别是因为另一个人(mmorrisson)试图做同样的事情。

本质上,我将_setFirehoseInstance方法添加到我的记录器类中,该方法仅从我的测试代码中调用,该代码用我自己的简单模拟类替换了firehose实例(生产代码中将调用新的AWS.Firehose())。

在我的测试代码中... let firehoseMock = {};

在beforeEach()创建并设置模拟来替换actualfirehose实例并在afterEach()恢复。

beforeEach(function (done) { 
    logger = new Logger({class: "Unit Test"}); 
    firehose = logger.getFirehoseInstance(); 
    consoleMock = sinon.mock(console); 

    firehoseMock.putRecord = function(params, callback) { 
     let recordIdObj = {"RecordId": recordIdVal}; 
     callback(null, recordIdObj); 
    };   
    logger._setFirehoseInstance(firehoseMock);  
    sinon.spy(firehoseMock, "putRecord"); 

    done(); 
}); 

afterEach(function (done) { 
    firehoseMock.putRecord.restore(); 
    logger._setFirehoseInstance(firehose); 
    consoleMock.restore(); 
    done(); 
}); 

而且在测试代码,我们尝试登录,或检查firehoseMock.putRecord被称为不...

it("Test AWS firehose API invoked", function (done) { 

    logger.setMode("production"); 
    logger.setEnvironment("test"); 

    logger.logInfo({ prop1: "value1" }, function(data){ 
     expect(firehoseMock.putRecord.calledOnce).to.equal(true); // should have been called once 
     expect(data.RecordId).to.equal(recordIdVal); // should have matching recordId 

     done(); 
    }); 

}); 

在生产代码,在记录器类有getter和setter对于firehose实例。

/** 
* Get's a AWS Firehose Instance 
*/ 
getFirehoseInstance() { 
    if (!(this.firehose)) { 
     this.firehose = new AWS.Firehose({apiVersion: Config.apiVersion, region: Config.region}); 
    } 
    return this.firehose; 
} 

/** 
* Set a AWS Firehose Instance - for TEST purose 
*/ 
_setFirehoseInstance(firehoseInstance) { 
    if (firehoseInstance) { 
     this.firehose = firehoseInstance; 
    } 
} 

这对我有效。当记录器在生产中调用firehose实例方法时,它将转到AWS服务,但在我调用log方法的单元测试中,它会在模拟上调用putRecord,因为我已用模拟替换了流水实例。然后,我可以适当地测试putRecord是否被调用(使用sinon.spy)。