How can I mock this http request using jest? - node.js

I am new to using Jest for unit tests. How can I mock this simple http request method "getData"? Here is the class:
const got = require("got")
class Checker {
constructor() {
this.url
this.logData = this.logData.bind(this);
this.getData = this.getData.bind(this);
}
async getData(url) {
const response = await got(url);
const data = await response.body;
return data;
}
async logData(first, second, threshold) {
let data = await this.getData(this.url)
console.log("received " + data.body);
}
}
I am trying to mock "getData" so I can write a unit test for "logData". Do I need to mock out the entire "got" module? Thanks.

If you change invoking got to got.get you should be able to have a working test like so:
const got = require('got');
const Checker = require('../index.js');
describe("some test", () => {
beforeEach(() => {
jest.spyOn(got, 'get').mockResolvedValue({ response: { body: { somekey: "somevalue" } } } );
});
it("works", async () => {
new Checker().getData();
expect(got.get).toBeCalledTimes(1);
})
})

One approach is to use dependency injection. Instead of calling 'got' directly, you can 'ask for it' in the class constructor and assign it to a private variable. Then, in the unit test, pass a mock version instead which will return what you want it to.
const got = require("got");
class Checker {
constructor(gotService) {
this.got = gotService;
this.logData = this.logData.bind(this);
this.getData = this.getData.bind(this);
}
async getData(url) {
const response = await this.got(url);
const data = await response.body;
return data;
}
async logData(first, second, threshold) {
let data = await this.getData(this.url)
console.log("received " + data.body);
}
}
//real code
const real = new Checker(got);
//unit testable code
const fakeGot = () => Promise.resolve(mockedData);
const fake = new Checker(fakeGot);
Here is what we are doing:
'Inject' got into the class.
In the class, call our injected version instead of directly calling the original version.
When it's time to unit test, pass a fake version which does what you want it to.

You can include this directly inside your test files. Then trigger the test that makes the Http request and this will be provided as the payload.
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: { eth: 0.6, btc: 0.02, ada: 1 } }),
})
);
it('should return correct mock token values', async () => {
const addresses = ["mockA", "mockB", "mockC"];
const res = await getTokenData(addresses);
expect(res.data).toEqual({ eth: 0.6, btc: 0.02, ada: 1 });
});

Related

Not able to cover multiple model function in jest test node js

I am new in node and jest unit testing. I have a method in controller where i am calling multiple model function and i want to cover all below query related to that particular method.
**Control code**
`interviewTypes = await InterviewType.findAll({});
interviewMediums = await InterviewMedium.findAll({});
toBeSortedinterviewDurations = await InterviewDuration.findAll({});`
test file
`it("Test getAllInterviewSlotDetails for branch sequelize", async () => {
let thrownError;
let param = {
sequelize: true
}
let getModelMock = getModels.mockImplementation(async () => {
return {};
});
let getMock = new getModelMock(sequelize);
try {
await interviews.getAllInterviewSlotDetails(param);
} catch (e) {
thrownError = e;
}
});`

How to mock a class that belongs to a module dependency? (And ensure that certain method inside the mocked class has been called) Jest - NodeJs

I am trying to mock the S3 class inside the aws-sdk module while macking sure that the methods inside the class S3 can be spyed.
I am able to mock the S3 class inside aws-sdk however I cannot spy the methods inside the class.
Any ideas on how to approach this problem?
These are my code snippets:
services/s3.js
const AWS = require('aws-sdk');
const uploadAsset = async (param) => {
try {
const response = await s3.upload(param).promise();
return response;
} catch (e) {
console.log(e);
}
}
module.exports = { uploadAsset }
services.s3.test.js
const AWS = require('aws-sdk');
const { uploadAsset } = require('../services/s3')
jest.mock('aws-sdk', () => {
return {
S3: class {
constructor() { }
upload(param) { // 👈 I want to make sure that this method is called
return {
promise: () => {
return Promise.resolve(
{
Location: `http://${param.Bucket}.s3.amazonaws.com/${param.Key}`,
Key: param.Key
}
)
}
}
}
}
}
});
describe('uploadAsset() functionality', () => {
it('should upload an asset', async () => {
const uploadPath = 'users/profilePicture';
const base64Str = '/9j/4AAQSkZJRgABAQAAAQABAAD/';
const buffer = Buffer.from(base64Str, 'base64');
const s3 = new AWS.S3();
const response = await uploadAsset({
Bucket: 'BucketName,
Key: `KeyName`,
Body: buffer,
});
const spy = jest.spyOn(s3, 'deleteObject')
expect(spy).toBeCalled(); // 🚨 This spy nevers gets called
});
});
Any insights would be helpful.
Thanks.
I mocked the aws-sdk successfully. However my spy in the S3 never gets called.
I am almost positive that this is a scope problem. I think my spyOn method only affects my local S3 class instance. However I still have no idea how to test this specific case scenario.

Unable to stub an exported function with Sinon

I need to test the following createFacebookAdVideoFromUrl() that consumes a retryAsyncCall that I'd like to stub with Sinon :
async function createFacebookAdVideoFromUrl(accountId, videoUrl, title, facebookToken = FACEBOOK_TOKEN, options = null, businessId = null) {
const method = 'POST';
const url = `${FACEBOOK_URL}${adsSdk.FacebookAdsApi.VERSION}/${accountId}/advideos`;
const formData = {
access_token: businessId ? getFacebookConfig(businessId).token : facebookToken,
title,
name: title,
file_url: videoUrl,
};
const callback = () => requestPromise({ method, url, formData });
const name = 'createFacebookAdVideoFromUrl';
const retryCallParameters = buildRetryCallParameters(name, options);
const adVideo = await retryAsyncCall(callback, retryCallParameters);
logger.info('ADVIDEO', adVideo);
return { id: JSON.parse(adVideo).id, title };
}
This retryAsyncCall function is exported as such:
module.exports.retryAsyncCall = async (callback, retryCallParameters, noRetryFor = [], customRetryCondition = null) => {
// Implementation details ...
}
Here is how I wrote my test so far:
it.only("should create the video calling business's Facebook ids", async () => {
const payload = createPayloadDataBuilder({
businessId: faker.internet.url(),
});
const retryAsyncCallStub = sinon.stub(retryAsyncCallModule, 'retryAsyncCall').resolves('random');
const createdFacebookAd = await FacebookGateway.createFacebookAdVideoFromUrl(
payload.accountId,
payload.videoUrl,
payload.title,
payload.facebookToken,
payload.options,
payload.businessId,
);
assert.strictEqual(retryAsyncCallStub.calledOnce, true);
assert.strictEqual(createdFacebookAd, { id: 'asdf', title: 'asdf' });
});
I don't expect it to work straightaway as I am working in TDD fashion, but I do expect the retryAsyncCall to be stubbed out. Yet, I am still having this TypeError: Cannot read property 'inc' of undefined error from mocha, which refers to an inner function of retryAsyncCall.
How can I make sinon stubbing work?
I fixed it by changing the way to import in my SUT :
// from
const { retryAsyncCall } = require('../../../helpers/retry-async');
// to
const retry = require('../../../helpers/retry-async');
and in my test file :
// from
import * as retryAsyncCallModule from '../../../src/common/helpers/retry-async';
// to
import retryAsyncCallModule from '../../../src/common/helpers/retry-async';
The use of destructuring seemed to make a copy instead of using the same reference, thus, the stub was not applied on the right reference.

mock transaction in runTransaction

i want to mock code inside a runTransaction function.
example code:
await admin.firestore().runTransaction(async transaction => {
const hubDocument = admin.firestore().collection("Acme").doc('4');
const hubData = (await transaction.get(hubDocument)).data();
newData = {
...hubData,
someAttribute: 'some new value'
};
transaction.update(hubDocument, newData);
})
i want to mock transaction, check if it is called with the right data etc.pp.
I managed to mock firestore() but do not know how to mock the transaction parameter.
I have not tested this, but I asume something like this should do the trick:
import { Transaction } from '#google-cloud/firestore';
const origTransactionGet = Transaction.prototype.get
Transaction.prototype.get = function () {
console.log(arguments, "< Intercepted Transaction.prototype.get")
return origTransactionGet.apply(this, arguments)
}
// your code
await admin.firestore().runTransaction(async transaction => {
const hubDocument = admin.firestore().collection("Acme").doc('4');
const hubData = (await transaction.get(hubDocument)).data();
newData = {
...hubData,
someAttribute: 'some new value'
};
transaction.update(hubDocument, newData);
})
As #FiodorovAndrei commented, the alternative, perhaps more comfortable if you use jest would be to just use firestore-jest-mock to mock the Firestore functionality.

sinon stub fails for promise functions if not exported within class

I'm trying to get sinon.stub to work for async function. I have created promiseFunction.js:
let functionToBeStubbed = async function() {
return ("Text to be replaced by stub.");
};
let promiseFunction = async function() {
return(await functionToBeStubbed());
};
module.exports = {
promiseFunction: promiseFunction,
functionToBeStubbed: functionToBeStubbed
};
and test promiseFunction.spec.js:
let functionstobestested = require('./promiseFunction.js');
describe('Sinon Stub Test', function () {
var sandbox;
it('should return --Text to be replaced by stub.--', async function () {
let responsevalue = "The replaced text.";
sandbox = sinon.sandbox.create();
sandbox.stub(functionstobestested, 'functionToBeStubbed').resolves(responsevalue);
//sandbox.stub(functionstobestested, 'functionToBeStubbed').returns(responsevalue);
let result = "Empty";
console.log(`BEFORE: originaldata = ${result}, value = ${responsevalue}`);
result = await functionstobestested.promiseFunction();
console.log(`AFTER: originaldata = ${result}, value = ${responsevalue}`);
expect(result).to.equal(responsevalue);
sandbox.restore();
console.log("AFTER2: Return value after restoring stub: " + await functionstobestested.promiseFunction());
});
});
when running the test, I will get
test failure
If I modify export slightly, it still fails:
var functionsForTesting = {
promiseFunction: promiseFunction,
functionToBeStubbed: functionToBeStubbed
};
module.exports = functionsForTesting;
I do not understand why this test fails, as it should pass. If I change the way I export functions from promiseFunction.js - module, the test pass correctly. Revised promiseFunction.js:
const functionsForTesting = {
functionToBeStubbed: async function() {
return ("Text to be replaced by stub.");
},
promiseFunction: async function() {
return(await functionsForTesting.functionToBeStubbed());
};
module.exports = functionsForTesting;
Test pass
What's wrong in my original and modified way to export functions?

Resources