如何在 node.js 单元测试中模拟构造函数调用?
How to mock a constructor call in a node.js unit test?
我编写了一个小 node.js 模块来使用 Twit 库更新 Twitter
// 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;
我已经使用 mocha、chai 和 sinon 为此编写了一个单元测试。但是对 Twit 的构造函数的调用总是返回实际对象——我如何用单元测试中的模拟对象替换它?
// 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 不支持这样的 class 存根构造函数。您需要使用 Link Seams, this is the CommonJS version, so we will be using proxyquire 来构造我们的接缝。
例如
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,
);
});
});
测试结果:
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
------------------|---------|----------|---------|---------|-------------------
我编写了一个小 node.js 模块来使用 Twit 库更新 Twitter
// 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;
我已经使用 mocha、chai 和 sinon 为此编写了一个单元测试。但是对 Twit 的构造函数的调用总是返回实际对象——我如何用单元测试中的模拟对象替换它?
// 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 不支持这样的 class 存根构造函数。您需要使用 Link Seams, this is the CommonJS version, so we will be using proxyquire 来构造我们的接缝。
例如
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,
);
});
});
测试结果:
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
------------------|---------|----------|---------|---------|-------------------