简体   繁体   中英

How to mock a constructor call in a node.js unit test?

I've written a small node.js module to update twitter using the Twit library

// src/twitterHelper.js
const Twit = require('twit');
const twitterClient = new Twit({
  consumer_key: 'consumer_key',
  consumer_secret: 'consumer_secret',
  access_token: 'access_token',
  access_token_secret: 'access_token_secret',
});

function updateStatus(params, callback) {
  twitterClient.post('statuses/update', params, function (err, data, response) {
    if (err) {
      console.log(`Error occurred updating status\t${err}`);
    } else {
      console.log(`Posted twitter with id ${data.id_str}`);
    }
  });
}

exports.updateStatus = updateStatus;

and I've written a unit test for this using mocha, chai, and sinon. But the call to the constructor for Twit is always returning the actual object - how I can replace this with the mock object from the unit test?

// test/twitterHelper.spec.js
const Twit = require('twit')
const expect = require("chai").expect;
const sinon     = require('sinon');
const twitterHelper = require("../src/twitterHelper");

describe("Unit tests for twitter helper", () => {
    const mockTwit = {
        post: (endpoint, params, callback) => {
            console.log(`Called ${endpoint} with ${params}`);
        }
    };

    it("should create a mock twit object", () => { 
        sinon.stub(Twit, 'constructor').returns(mockTwit);
        twitterHelper.updateStatus({status: "New status"});
    });
});

Sinon does NOT support stub constructor of a class like that. You need to use Link Seams , this is the CommonJS version, so we will be using proxyquire to construct our seams.

Eg

twitterHelper.js :

const Twit = require('twit');
const twitterClient = new Twit({
  consumer_key: 'consumer_key',
  consumer_secret: 'consumer_secret',
  access_token: 'access_token',
  access_token_secret: 'access_token_secret',
});

function updateStatus(params, callback) {
  twitterClient.post('statuses/update', params, function (err, data, response) {
    if (err) {
      console.log(`Error occurred updating status\t${err}`);
    } else {
      console.log(`Posted twitter with id ${data.id_str}`);
    }
  });
}

exports.updateStatus = updateStatus;

twitterHelper.test.js :

const sinon = require('sinon');
const proxyquire = require('proxyquire');

describe('Unit tests for twitter helper', () => {
  it('should create a mock twit object', () => {
    const twitInstanceStub = { post: sinon.stub() };
    const TwitStub = sinon.stub().returns(twitInstanceStub);
    const twitterHelper = proxyquire('./twitterHelper', {
      twit: TwitStub,
    });
    twitterHelper.updateStatus({ status: 'New status' });
    sinon.assert.calledWithExactly(TwitStub, {
      consumer_key: 'consumer_key',
      consumer_secret: 'consumer_secret',
      access_token: 'access_token',
      access_token_secret: 'access_token_secret',
    });
    sinon.assert.calledWithExactly(
      twitInstanceStub.post,
      'statuses/update',
      { status: 'New status' },
      sinon.match.func,
    );
  });
});

test result:

  Unit tests for twitter helper
    ✓ should create a mock twit object (1506ms)


  1 passing (2s)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------|---------|----------|---------|---------|-------------------
All files         |   57.14 |        0 |      50 |   57.14 |                   
 twitterHelper.js |   57.14 |        0 |      50 |   57.14 | 11-14             
------------------|---------|----------|---------|---------|-------------------

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM