2017-04-10 64 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)。