Reputation: 443
I am trying to write unit tests for GCP cloud functions.Below is the code snippet that I am trying to test.
//index.js
const {PubSub} = require('@google-cloud/pubsub');
const pubsub = new PubSub();
exports.myFunction = functions.runWith(RUNTIME_OPTS).https.onCall(async (data, context) => {
//pubsubbody = generated..
//some logic
await pubsub.topic(topicname).publish(pubsubBody, {
platform: body.platform,
environment: body.environment,
event: !!event ? event : 'unknown',
});
});
I was able to test myFunction logic using firebase-functions-test library. I did something like this
//test.js
const fft = require('firebase-functions-test')();
const funcs = require('../../index');
describe('function to test viewAddRequestedProxy', () => {
const wrapped = fft.wrap(funcs.viewAddRequestedProxy);
// simple logic test.
test('NullDataTest', async () => {
const result = await wrapped(null, null);
expect(result).toStrictEqual({output: 'xyz'});
});
});
Now I want to test the line where I make a pubsub call inside my function. I tried many things but I am really new to node.js as well as mocking objects in general.
I am not understanding how can I mock the pubsub object inside my index.js and check if it is publishing message or not.
await pubsub.topic(topicname).publish(pubsubBody, {
platform: body.platform,
environment: body.environment,
event: !!event ? event : 'unknown',
});
I am confused if I need to export my pubsub object and access it test.js or not. I tried this but for some reason "module.exports = { pubsub: pubsub };"
was not working for me.
I also tried to create a mock in test.js using spyOn
const {PubSub} = require('@google-cloud/pubsub');
const singleAdd = (PubSub) => {
PubSub.topic;
};
test('spyOn .toBeCalled()', (object, method) => {
const somethingSpy = jest.spyOn(PubSub, 'topic');
somethingSpy();
const topic = PubSub.topic('abc');
expect(somethingSpy).toBeCalled();
});
Also tried manual mocks although I am not sure if I need this. By creating a Mocks folder but nothing seems to work.
Any help will be appreciated. I think I am completely off track.
Upvotes: 2
Views: 4935
Reputation: 11
ReferenceError: spyOn is not defined when using above method to mock pubsub
Upvotes: -1
Reputation: 35553
In order to mock a node lib you need to put a file inside __mocks__/lib-name/index.js
which will mimic the real interface.
In your case, put a pubsub.js
file inside __mocks__/@google-cloud/
.
//pubsub.js
class PubSubMock {
static mockInstances = [];
static clearAllMocks() {
PubSubMock.mockInstances.forEach((instance) =>
Object.getOwnPropertyNames(
instance.constructor.prototype
).forEach((method) => method.mockClear())
);
PubSubMock.mockInstances.length = 0;
}
constructor() {
Object.getOwnPropertyNames(this.constructor.prototype).forEach((method) => {
spyOn(this, method).and.callThrough();
});
PubSubMock.mockInstances.push(this);
}
topic(topic) {
// you can implement here the logic
return this;
}
publish(body, obj) {
return this;
}
}
module.exports.PubSub = PubSubMock;
Jest will make sure that each require of '@google-cloud/pubsub'
will be mapped to that mock file.
The mock implementation of the PubSub
class is auto spying on all of the class methods, this way you can test if some method is called and with what args.
The problem is that your production code creates a new instance of PubSub
and not exposing it, therefore you can't get a reference for it. A small trick is to expose a static property which will hold all created instances, and will be exposed by PubSub.mockInstances
.
const {PubSub} = require('@google-cloud/pubsub');
const funcs = require('../../index');
test('spyOn .toBeCalled()', (object, method) => {
funcs() // call for you real code
const mockInstance = PubSub.mockInstances[0];
expect(mockInstance.topic).toHaveBeenCalledWith('arg1');
});
Upvotes: 5