Stub of Sinon using axios returns undefined - javascript

I'm testing using sinon with axios.
// index.js
{
.. more code
const result = await axios.get("http://save")
const sum = result.data.sum
}
And I made a test code by sinon and supertest for e2e test.
// index.test.js
describe('ADMINS GET API, METHOD: GET', () => {
it('/admins', async () => {
sandbox
.stub(axios, 'get')
.withArgs('http://save')
.resolves({sum: 12});
await supertest(app)
.get('/admins')
.expect(200)
.then(async response => {
expect(response.body.code).toBe(200);
});
});
});
But when I test it, it gives me this result.
// index.js
{
.. more code
const result = await axios.get("http://save")
const sum = result.data.sum
console.log(sum) // undefined
}
I think I resolved response. But it doesnt' give any response.
It just pass axios on supertest.
How can I return correct data in this case?
Thank you for reading it.

The resolved value should be { data: { sum: 12 } }.
E.g.
index.js:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/admins', async (req, res) => {
const result = await axios.get('http://save');
const sum = result.data.sum;
res.json({ code: 200, sum });
});
module.exports = { app };
index.test.js:
const supertest = require('supertest');
const axios = require('axios');
const sinon = require('sinon');
const { app } = require('./index');
describe('ADMINS GET API, METHOD: GET', () => {
it('/admins', async () => {
const sandbox = sinon.createSandbox();
sandbox
.stub(axios, 'get')
.withArgs('http://save')
.resolves({ data: { sum: 12 } });
await supertest(app)
.get('/admins')
.expect(200)
.then(async (response) => {
sinon.assert.match(response.body.code, 200);
sinon.assert.match(response.body.sum, 12);
});
});
});
test result:
ADMINS GET API, METHOD: GET
✓ /admins
1 passing (22ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------

Related

test rejecting an axios promise

I am trying to figure out how to correctly mock this code. I have figured out how to mock a success, but I cannot figure out how to mock the catch/reject block.
App code:
function getData(url = '') {
return new Promise((resolve, reject) => {
axios
.get(`${baseURL}${url}`)
.then((res) => {
resolve(res.data);
})
.catch((err) => {
reject(err);
});
});
}
in testing I have:
import axios from 'axios';
const MockAdapter = require('axios-moc-adapter');
describe('getData', () => {
it('test success', async () => {
const mock = new MockAdapter(axios);
mock.onGet(`${service.baseURL}/sites`).reply(200, 'success');
const axiosSpy = jest.spyOn(axios, 'get');
const rtn = await service.getData('/sites');
expect(axiosSpy).toHaveBeenCalled();
expect(rtn).toBe('success');
});
it('test failure', async () => {
const mock = new MockAdapter(axios);
mock.onGet(`${service.baseURL}/sites`).networkError();
const rtn = await service.getData('/sites').catch((err) => {
expect(err.message).toBe('message');
});
expect(rtn).toBe(undefined);
});
});
The test success test works, but when I test the failure, I get:
Expected: "message"
Received: "Cannot read properties of undefined (reading 'then')"
29 | mock.onGet(`${service.baseURL}/sites`).networkError();
30 | const rtn = await service.getData('/sites').catch((err) => {
> 31 | expect(err.message).toBe('message');
| ^
32 | });
33 | expect(rtn).toBe(undefined);
34 | });
How can I write a test that successfully passes the failure case? Specifically, I can make this test pass by not checking inside the catch block, but when I run coverage, it doesn't cover the catch block in the App code. How can I correctly cover this line?
Can't reproduce your issue, but .networkError() will throw an error with 'Network Error' message rather than 'message', see source code index.js#L141
service.ts:
import axios from 'axios';
export const baseURL = 'http://localhost:3000/api';
export function getData(url = '') {
return new Promise((resolve, reject) => {
axios
.get(`${baseURL}${url}`)
.then((res) => {
resolve(res.data);
})
.catch((err) => {
reject(err);
});
});
}
service.test.ts:
import * as service from './service';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
describe('getData', () => {
it('test success', async () => {
const mock = new MockAdapter(axios);
mock.onGet(`${service.baseURL}/sites`).reply(200, 'success');
const axiosSpy = jest.spyOn(axios, 'get');
const rtn = await service.getData('/sites');
expect(axiosSpy).toHaveBeenCalled();
expect(rtn).toBe('success');
});
it('test failure', async () => {
const mock = new MockAdapter(axios);
mock.onGet(`${service.baseURL}/sites`).networkError();
const rtn = await service.getData('/sites').catch((err) => {
expect(err.message).toBe('Network Error');
});
expect(rtn).toBe(undefined);
});
it('test failure 2', async () => {
const mock = new MockAdapter(axios);
mock.onGet(`${service.baseURL}/sites`).networkError();
await expect(service.getData('/sites')).rejects.toThrowError('Network Error');
});
});
test result:
PASS examples/69947398/service.test.ts (8.89 s)
getData
✓ test success (5 ms)
✓ test failure (1 ms)
✓ test failure 2 (2 ms)
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 100 | 0 | 100 | 100 |
service.ts | 100 | 0 | 100 | 100 | 4
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 9.52 s

Stubing AWS SQS client

Im trying to stub aws-sdk/client-sqs calls. Im both new to aws and stubbing and I cant find much in the docs to do with stubbing using mocha/chai
My file
import { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } from '#aws-sdk/client-sqs'
const sqsClient = new SQSClient()
const params = {
AttributeNames: ['SentTimestamp'],
MaxNumberOfMessages: 10,
QueueUrl: paymentsUrl,
//....more params
}
const getPolledMessages = async sqsClient => {
const data = await sqsClient.send(new ReceiveMessageCommand(params))
//....continue to do stuff with the data
}
My test is as below. I heavily borrowed from this test which stubs #aws-sdk/client-ses
import { ReceiveMessageCommand, SQSClient } from '#aws-sdk/client-sqs'
import { expect } from 'chai'
describe('mock sqs', () => {
let stub;
before(() => {
stub = sinon.stub(SQSClient.prototype, 'ReceiveMessageCommand')
})
after(() => stub.restore())
it('can send', async () => {
await SQSClient.send(new ReceiveMessageCommand(params))
expect(stub.calledOnce).to.be.true
})
})
Currently getting the following error
TypeError: Cannot stub non-existent property ReceiveMessageCommand
at Function.stub (node_modules/sinon/lib/sinon/stub.js:73:15)
at Sandbox.stub (node_modules/sinon/lib/sinon/sandbox.js:333:37)
You can stub out dependencies with link seams.. We should use proxyquire package to do that.
E.g.
index.ts:
import { SQSClient, ReceiveMessageCommand } from '#aws-sdk/client-sqs';
const sqsClient = new SQSClient({ region: 'REGION' });
const params = {
AttributeNames: ['SentTimestamp'],
MaxNumberOfMessages: 10,
QueueUrl: 'paymentsUrl',
};
export const getPolledMessages = async () => {
const data = await sqsClient.send(new ReceiveMessageCommand(params));
};
index.test.ts:
import proxyquire from 'proxyquire';
import sinon from 'sinon';
describe('68017252', () => {
it('should pass', async () => {
const sqsClientInstance = {
send: sinon.stub(),
};
const SQSClient = sinon.stub().returns(sqsClientInstance);
const ReceiveMessageCommand = sinon.stub();
const { getPolledMessages } = proxyquire('./', {
'#aws-sdk/client-sqs': {
SQSClient,
ReceiveMessageCommand,
},
});
await getPolledMessages();
sinon.assert.calledWithExactly(SQSClient, { region: 'REGION' });
sinon.assert.calledOnce(sqsClientInstance.send);
sinon.assert.calledWithExactly(ReceiveMessageCommand, {
AttributeNames: ['SentTimestamp'],
MaxNumberOfMessages: 10,
QueueUrl: 'paymentsUrl',
});
});
});
test result:
68017252
✓ should pass (3331ms)
1 passing (3s)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------

how to test cheerio js

this is my code and i don't know how to write test for it:
html.js
const getHtml = async (url) => {
const { data } = await axios.get(url);
return data;
};
const cheerioInit = async (url) => cheerio.load(await getHtml(url));
module.exports = {
cheerioInit,
getHtml
};
i think i should mock this, but don't know how to do that. i wrote this but getting error:
const htmlJs = require("./html");
describe("html", () => {
it("initializes cheerio js", () => {
const mock = jest.spyOn(htmlJs, "cheerioInit");
expect(mock).toHaveBeenCalled();
});
});
this is error:
You can use jest.spyOn(object, methodName) to mock axios.get() method and its resolved value and mock cheerio.load() method with a fake implementation. After executing the getHtml and cheerioInit functions, make assertion for above mocks to check if they have been called with specific arguments.
E.g.
html.js:
const axios = require('axios');
const cheerio = require('cheerio');
const getHtml = async (url) => {
const { data } = await axios.get(url);
return data;
};
const cheerioInit = async (url) => cheerio.load(await getHtml(url));
module.exports = {
cheerioInit,
getHtml,
};
html.test.js:
const { getHtml, cheerioInit } = require('./html');
const axios = require('axios');
const cheerio = require('cheerio');
describe('html', () => {
afterEach(() => {
jest.restoreAllMocks();
});
describe('getHtml', () => {
it('should get html', async () => {
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: '<div>teresa teng</div>' });
const actual = await getHtml('http://localhost:3000');
expect(actual).toEqual('<div>teresa teng</div>');
expect(getSpy).toBeCalledWith('http://localhost:3000');
});
});
describe('cheerioInit', () => {
it('should initializes cheerio', async () => {
const loadSpy = jest.spyOn(cheerio, 'load').mockImplementation();
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: '<div>teresa teng</div>' });
await cheerioInit('http://localhost:3000');
expect(loadSpy).toHaveBeenCalledWith('<div>teresa teng</div>');
});
});
});
unit test result:
PASS examples/66304918/html.test.js
html
getHtml
✓ should get html (3 ms)
cheerioInit
✓ should initializes cheerio
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
html.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.088 s

Stub a standalone module.exports function using rewire

I am trying to stub a module.exports function. But I have some trouble. I will give you a sudo code of the situation.
MyController.js
const sendOTPOnPhone = rewire('../../src/services/OtpService/sendOTPOnPhone')
module.exports = async function(req, res) {
const { error, data } = await sendOTPOnPhone(req.query.phone) //this is I want to stub
if(error)
return return res.send(error)
return res.send(data)
}
sendOTPService.js
module.exports = async function(phone) {
const result = await fetch(`external-api-call`)
if(result.status !== 'success')
return {
error: "Failed to send OTP!",
data: null
}
return {
error: null,
data: result
}
}
sendOTPTest.js
const expect = require('chai').expect
const request = require('supertest')
const sinon = require('sinon')
const rewire = require('rewire')
const sendOTPOnPhone = rewire('../../src/services/OtpService/sendOTPOnPhone')
const app = require('../../src/app')
describe('GET /api/v1/auth/otp/generate', function () {
it('should generate OTP', async () => {
let stub = sinon.stub().returns({
error: null,
data: "OTP sent"
})
sendOTPOnPhone.__set__('sendOTPOnPhone', stub)
const result = await request(app)
.get('/api/v1/auth/otp/generate?phone=8576863491')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
expect(stub.calledOnce).to.be.true
console.log(result.body)
// expect(result).to.equal('promise resolved');
})
})
Above test is failing, stub is not being called. I don't know what am I missing? If I do this in my sendOTPService:
const sendOTP = async function() {}
module.exports = {
sendOTP
}
and this in the controller.
const { error, data } = sendOTPOnPhone.sendOTPOnPhone(req.query.phone)
It works.
But I import it like const {sendOTPOnPhone } = require('../sendOTPService') It doesn't work.
I am aware that destructing changes the reference of the object.
Can someone suggest a workaround?
Is it possible to achieve this using rewire? OR It can be done with proxyquire.Please can someone suggest?
Here is the integration testing solution using proxyquire, you should use Globally override require.
app.js:
const express = require('express');
const controller = require('./controller');
const app = express();
app.get('/api/v1/auth/otp/generate', controller);
module.exports = app;
controller.js:
let sendOTPOnPhone = require('./sendOTPOnPhone');
module.exports = async function(req, res) {
const { error, data } = await sendOTPOnPhone(req.query.phone);
if (error) return res.send(error);
return res.send(data);
};
sendOTPOnPhone.js:
module.exports = async function(phone) {
const result = await fetch(`external-api-call`);
if (result.status !== 'success')
return {
error: 'Failed to send OTP!',
data: null,
};
return {
error: null,
data: result,
};
};
sendOTP.test.js:
const request = require('supertest');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('GET /api/v1/auth/otp/generate', function() {
it('should generate OTP', async () => {
let stub = sinon.stub().resolves({
error: null,
data: { message: 'OTP sent' },
});
stub['#global'] = true;
const app = proxyquire('./app', {
'./sendOTPOnPhone': stub,
});
const result = await request(app)
.get('/api/v1/auth/otp/generate?phone=8576863491')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
sinon.assert.calledOnce(stub);
console.log(result.body);
});
});
Integration test results with coverage report:
GET /api/v1/auth/otp/generate
{ message: 'OTP sent' }
✓ should generate OTP (2373ms)
1 passing (2s)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 68.75 | 25 | 50 | 73.33 |
app.js | 100 | 100 | 100 | 100 |
controller.js | 83.33 | 50 | 100 | 100 | 5
sendOTPOnPhone.js | 20 | 0 | 0 | 20 | 2-4,8
-------------------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60599945
I have answered a similar question here stub SMS otp method (without using external dependecy like proxyquire)
basically, the problem is with exports. Move your sendOtp method to a generalized place e.g User Model or schema. Import model, stub its function. It should work fine.
you are stubbing imported property called sendOtp instead of original function.

TypeError: sns.publish is not a function , jest mock AWS SNS

I am trying to mock AWS.SNS and I am getting error. I referred posts on StackOverflow and could come up with below. Still, I am getting an error. I have omitted the irrelevant portion. Can someone please help me?
Below is my index.ts
import { SNS } from "aws-sdk";
export const thishandler = async (event: thisSNSEvent): Promise<any> => {
// I have omitted other code that works and not related to issue I am facing.
// I am receiving correct value of 'snsMessagetoBeSent' I verified that.
const response = await sendThisToSNS(snsMessagetoBeSent);
} // thishandler ends here
async function sendThisToSNS(thisMessage: snsAWSMessage) {
const sns = new SNS();
const TOPIC_ARN = process.env.THIS_TOPIC_ARN;
var params = {
Message: JSON.stringify(thisMessage), /* required */
TopicArn: TOPIC_ARN
};
return await sns.publish(params).promise();
}
My test case is below
jest.mock('aws-sdk', () => {
const mockedSNS = {
publish: jest.fn().mockReturnThis(),
promise: jest.fn()
};
return {
SNS: jest.fn(() => mockedSNS),
};
});
import aws, { SNS } from 'aws-sdk';
const snsPublishPromise = new aws.SNS().publish().promise;
import { thishandler } from "../src/index";
describe("async testing", () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it("async test", async () => {
const ENRICHER_SNS_TOPIC_ARN = process.env.ENRICHER_SNS_TOPIC_ARN;
process.env.ENRICHER_SNS_TOPIC_ARN = "OUR-SNS-TOPIC";
const mockedResponseData ={
"Success": "OK"
};
(snsPublishPromise as any).mockResolvedValueOnce(mockedResponseData);
const result = await thishandler(thisSNSEvent);
});
I get error as TypeError: sns.publish is not a function
Here is the unit test solution:
index.ts:
import { SNS } from 'aws-sdk';
export const thishandler = async (event): Promise<any> => {
const snsMessagetoBeSent = {};
const response = await sendThisToSNS(snsMessagetoBeSent);
return response;
};
async function sendThisToSNS(thisMessage) {
const sns = new SNS();
const TOPIC_ARN = process.env.THIS_TOPIC_ARN;
const params = {
Message: JSON.stringify(thisMessage),
TopicArn: TOPIC_ARN,
};
return await sns.publish(params).promise();
}
index.test.ts:
import { thishandler } from './';
import { SNS } from 'aws-sdk';
jest.mock('aws-sdk', () => {
const mSNS = {
publish: jest.fn().mockReturnThis(),
promise: jest.fn(),
};
return { SNS: jest.fn(() => mSNS) };
});
describe('59810802', () => {
let sns;
beforeEach(() => {
sns = new SNS();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should pass', async () => {
const THIS_TOPIC_ARN = process.env.THIS_TOPIC_ARN;
process.env.THIS_TOPIC_ARN = 'OUR-SNS-TOPIC';
const mockedResponseData = {
Success: 'OK',
};
sns.publish().promise.mockResolvedValueOnce(mockedResponseData);
const mEvent = {};
const actual = await thishandler(mEvent);
expect(actual).toEqual(mockedResponseData);
expect(sns.publish).toBeCalledWith({ Message: JSON.stringify({}), TopicArn: 'OUR-SNS-TOPIC' });
expect(sns.publish().promise).toBeCalledTimes(1);
process.env.THIS_TOPIC_ARN = THIS_TOPIC_ARN;
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/59810802/index.test.ts (13.435s)
59810802
✓ should pass (9ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.446s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59810802

Categories

Resources