如何使用 ProxyRequire 模拟 NodeJs 测试
How to use ProxyRequire to mock NodeJs test
我正在为以下文件编写单元测试用例
//filename: a.js
var amqp = require("amqplib");
class RMQ {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {
var connection = await amqp.connect(this.URI);
this.connection = connection;
return connection;
}
}
const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);
module.exports = {
RMQ_INSTANCE,
};
我正在使用下面文件
中的实例RMQ_INSTANCE
// filename: b.js
const { RMQ_INSTANCE } = require("./a");
module.exports.publishEmail = async function(message) {
var connection = await RMQ_INSTANCE.getInstance();
var channel = await connection.createChannel();
var exchange = "some_exchange";
var key = "some_key";
var msg = JSON.stringify(message);
await channel.assertExchange(exchange, "topic", { durable: false });
await channel.publish(exchange, key, Buffer.from(msg));
setTimeout(function () {
connection.close();
}, 500);
}
我正在使用 proxyrequire 模拟 b.js
中的 RMQ_INSTANCE
// filename: b.test.js
var proxyrequire = require("proxyquire").noCallThru();
var sinon = require("sinon");
const { assert } = require("sinon");
class fakeRMQClass {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {
var connection = getFakeRMQStub()
return connection;
}
}
var producerTest = function () {
it("producer connection - success test", async function () {
var fakeRMQInstance = new fakeRMQClass("fake_url");
var rmqUtils = proxyrequire("../path/to/b.js", {
"./a": fakeRMQInstance
});
await rmqUtils.publishEmail("fake_msg");
});
afterEach(function () {
sinon.verifyAndRestore();
});
};
describe("test_producer", producerTest);
但我发现模拟无法正常工作。谁能帮我正确地模拟这个?
因为 RMQ_INSTANCE
是一个对象,你可以使用 sinon.stub(obj, 'method')
存根它的方法,你不需要使用 proxyquire
包。
既然要测试b
模块,那么b
模块只关心它所依赖的RMQ_INSTANCE
接口,具体实现无所谓。
b.js
:
const { RMQ_INSTANCE } = require('./a');
module.exports.publishEmail = async function (message) {
var connection = await RMQ_INSTANCE.getInstance();
var channel = await connection.createChannel();
var exchange = 'some_exchange';
var key = 'some_key';
var msg = JSON.stringify(message);
await channel.assertExchange(exchange, 'topic', { durable: false });
await channel.publish(exchange, key, Buffer.from(msg));
setTimeout(function () {
connection.close();
}, 500);
};
a.js
:
class RMQ {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {}
}
const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);
module.exports = { RMQ_INSTANCE };
b.test.js
:
const sinon = require('sinon');
const { RMQ_INSTANCE } = require('./a');
const { publishEmail } = require('./b');
describe('test_producer', () => {
let clock;
before(() => {
clock = sinon.useFakeTimers();
});
after(() => {
clock.restore();
});
it('producer connection - success test', async () => {
const channelStub = {
assertExchange: sinon.stub().returnsThis(),
publish: sinon.stub(),
};
const connectionStub = { createChannel: sinon.stub().resolves(channelStub), close: sinon.stub() };
sinon.stub(RMQ_INSTANCE, 'getInstance').resolves(connectionStub);
await publishEmail('fake message');
sinon.assert.calledOnce(RMQ_INSTANCE.getInstance);
sinon.assert.calledOnce(connectionStub.createChannel);
sinon.assert.calledWithExactly(channelStub.assertExchange, 'some_exchange', 'topic', { durable: false });
sinon.assert.calledWithExactly(
channelStub.publish,
'some_exchange',
'some_key',
Buffer.from(JSON.stringify('fake message')),
);
clock.tick(500);
sinon.assert.calledOnce(connectionStub.close);
});
});
测试结果:
test_producer
✓ producer connection - success test
1 passing (10ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 75 | 100 |
a.js | 100 | 100 | 50 | 100 |
b.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
我正在为以下文件编写单元测试用例
//filename: a.js
var amqp = require("amqplib");
class RMQ {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {
var connection = await amqp.connect(this.URI);
this.connection = connection;
return connection;
}
}
const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);
module.exports = {
RMQ_INSTANCE,
};
我正在使用下面文件
中的实例RMQ_INSTANCE
// filename: b.js
const { RMQ_INSTANCE } = require("./a");
module.exports.publishEmail = async function(message) {
var connection = await RMQ_INSTANCE.getInstance();
var channel = await connection.createChannel();
var exchange = "some_exchange";
var key = "some_key";
var msg = JSON.stringify(message);
await channel.assertExchange(exchange, "topic", { durable: false });
await channel.publish(exchange, key, Buffer.from(msg));
setTimeout(function () {
connection.close();
}, 500);
}
我正在使用 proxyrequire 模拟 b.js
RMQ_INSTANCE
// filename: b.test.js
var proxyrequire = require("proxyquire").noCallThru();
var sinon = require("sinon");
const { assert } = require("sinon");
class fakeRMQClass {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {
var connection = getFakeRMQStub()
return connection;
}
}
var producerTest = function () {
it("producer connection - success test", async function () {
var fakeRMQInstance = new fakeRMQClass("fake_url");
var rmqUtils = proxyrequire("../path/to/b.js", {
"./a": fakeRMQInstance
});
await rmqUtils.publishEmail("fake_msg");
});
afterEach(function () {
sinon.verifyAndRestore();
});
};
describe("test_producer", producerTest);
但我发现模拟无法正常工作。谁能帮我正确地模拟这个?
因为 RMQ_INSTANCE
是一个对象,你可以使用 sinon.stub(obj, 'method')
存根它的方法,你不需要使用 proxyquire
包。
既然要测试b
模块,那么b
模块只关心它所依赖的RMQ_INSTANCE
接口,具体实现无所谓。
b.js
:
const { RMQ_INSTANCE } = require('./a');
module.exports.publishEmail = async function (message) {
var connection = await RMQ_INSTANCE.getInstance();
var channel = await connection.createChannel();
var exchange = 'some_exchange';
var key = 'some_key';
var msg = JSON.stringify(message);
await channel.assertExchange(exchange, 'topic', { durable: false });
await channel.publish(exchange, key, Buffer.from(msg));
setTimeout(function () {
connection.close();
}, 500);
};
a.js
:
class RMQ {
constructor(connectionURI) {
this.URI = connectionURI;
}
async getInstance() {}
}
const RMQ_INSTANCE = new RMQ(process.env.RMQ_URL);
module.exports = { RMQ_INSTANCE };
b.test.js
:
const sinon = require('sinon');
const { RMQ_INSTANCE } = require('./a');
const { publishEmail } = require('./b');
describe('test_producer', () => {
let clock;
before(() => {
clock = sinon.useFakeTimers();
});
after(() => {
clock.restore();
});
it('producer connection - success test', async () => {
const channelStub = {
assertExchange: sinon.stub().returnsThis(),
publish: sinon.stub(),
};
const connectionStub = { createChannel: sinon.stub().resolves(channelStub), close: sinon.stub() };
sinon.stub(RMQ_INSTANCE, 'getInstance').resolves(connectionStub);
await publishEmail('fake message');
sinon.assert.calledOnce(RMQ_INSTANCE.getInstance);
sinon.assert.calledOnce(connectionStub.createChannel);
sinon.assert.calledWithExactly(channelStub.assertExchange, 'some_exchange', 'topic', { durable: false });
sinon.assert.calledWithExactly(
channelStub.publish,
'some_exchange',
'some_key',
Buffer.from(JSON.stringify('fake message')),
);
clock.tick(500);
sinon.assert.calledOnce(connectionStub.close);
});
});
测试结果:
test_producer
✓ producer connection - success test
1 passing (10ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 75 | 100 |
a.js | 100 | 100 | 50 | 100 |
b.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------