【问题标题】:How to write sinon tests where third part api's are concerned如何编写涉及第三方api的sinon测试
【发布时间】:2017-05-22 17:28:21
【问题描述】:

我正在尝试测试只有一个功能的 geocode.js:

// geocode.js

const googleApiClient = require('@google/maps').createClient({
  key: CONFIG.googleApis.key
});

const poll = zipcode => {

  return new Promise((resolve, reject) => {

     googleApiClient.geocode({
        address: zipcode
      }, function(err, response) {
           resolve(response.json.results); // This is line 22
     });

   });

};

module.exports = {poll}

所以我已经设置了我的 mocha 环境并安装了 sinon。我不知道如何存根 googleApiClient 功能。

我实际上不想在测试期间拨打任何类型的外部电话。

 //geocode.spec.js
describe('geocode', function(){
   before(function(){

     //HOW DO I STUB googleApiClient ?
     sinon.stub ...

   })

});

【问题讨论】:

  • 替代 POV:不要模拟 googleApiClient,而是将客户端作为参数传递给 poll。然后,您可以使用 geocode 方法创建任何普通的旧对象,而不必对 googleApiClient 对象进行猴子补丁。

标签: node.js mocha.js sinon


【解决方案1】:

有点像 PITA,因为 createClient 每次都返回一个普通对象,而不是一个可以存根的类。

不过,我想出了这个:

const chai   = require('chai');
const expect = chai.expect;
const sinon  = require('sinon');

// This allows us to `expect` on Sinon properties.
chai.use(require('sinon-chai'));

// Load the Google Maps module.
const googleMaps = require('@google/maps');

// Create a dummy client.
const googleApiClient = googleMaps.createClient({ key : 'foo' });

// Stub `googleMaps.createClient` so it always returns the dummy client.
let createClientStub = sinon.stub(googleMaps, 'createClient').returns(googleApiClient);

// Load the code to be tested. This has to be done _after_ stubbing `createClient`.
const poll = require('./geocode.js').poll;

// The test.
describe('geocode', function(){
  let stub;

  // For each test, stub the `geocode` function.
  beforeEach(function() {
    stub = sinon.stub(googleApiClient, 'geocode');
  });

  // After each test, restore it to its original.
  afterEach(function() {
    stub.restore();
  });

  it('should call `googleApiClient.geocode` with the correct zipcode', function() {
    // This means that `googleApiClient.geocode()` will call the first function
    // argument that it gets passed, which is the callback function that
    // resolves the promise.
    stub.yields(null, { json : { results : 'foo' } });

    // Now we call the function under test, pass it a zipcode, and check if 
    // googleApiClient.geocode() got called with the correct argument.
    const ZIPCODE = '90210';
    return poll(ZIPCODE).then(zipcode => {
      expect(stub).to.be.calledWith({ address: ZIPCODE });
    });
  });

});

【讨论】:

  • @robertkley 谢谢你的详细解释。这有很大帮助。但是我无法理解为什么我不断收到此错误:** 1) 地理编码服务应使用正确的邮政编码调用 googleApiClient.geocode:TypeError: Cannot read property 'json' of undefined at app/services/geocode.service.js :22:25 **
  • @BubbleTrouble 取决于文件 app/services/geocode.service.js 的第 22 行的内容。
  • @robertkley 我已经更新了我上面的问题,以反映第 22 行的内容
  • @BubbleTrouble 啊,好的,geocode 的回调期望参数是特定格式的。我编辑了我的答案,唯一的变化是stub.yields() 的调用方式(它反映了回调将收到的参数,因此是假的“错误”和“响应”)。但是,它确实表明您的实现缺乏错误处理和无效响应(但这当然是您进行测试的原因)。
【解决方案2】:

你可以这样做

我将通过 HTTP 调用 API 的存根示例来展示它:

const httpCallStub = sinon.stub(HTTP, 'call');
const callback = sinon.spy();

// test case block

it('Testing function which has HTTP call API', function () {
  response.data = {
    // dummy api response
  };
  httpCallStub.yields(null, response);
  functionWhichhasThisHTTPCallAPI(callback);
  expect(callback.calledOnce).to.equal(true);
});

那么代码中发生了什么

  1. 基本上当您生成存根时,它会在实际调用该函数时传递这些参数
  2. 所以当我产生 HTTP get API 时,当它出现在测试用例中时,sinon 不会调用 HTTP API,而是将这 2 个参数作为该 API 调用的响应传递

如果清楚,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 2015-06-03
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多