How to mock the call of service.request in the code bellow?
import url from 'url'
import jayson from 'jayson/promise'
export async function dispatch(webHook, method, payload) {
const service = jayson.Client.https({ ...url.parse(webHook) })
return service.request(method, { ...payload })
}
In my unit-test I want to do something like this
jest.mock("") // what should go here?
it(() => {
const method = 'test'
expect(request).toHaveBeenCalledWith(method...) ?
})
UPDATE
I updated with my findings my code, but still no luck
import { Client } from 'jayson/promise'
import { dispatch } from '../src/remote'
jest.mock('jayson')
describe('remote', () => {
let spy: jest.SpyInstance<any>
beforeEach(() => {
spy = jest.spyOn(Client.https.prototype, 'request')
})
afterEach(() => {
spy.mockClear()
})
it('should invoke request method', () => {
const url = 'http://example.com:8000'
const method = ''
const payload = {}
dispatch(url, method, payload)
expect(spy).toHaveBeenCalledWith({})
})
})
You can use jest.mock to mock jayson/promise module. Don't need to use jest.spyOn.
E.g.
index.ts:
import url from 'url';
import jayson from 'jayson/promise';
export async function dispatch(webHook, method, payload) {
const service = jayson.Client.https({ ...url.parse(webHook) });
return service.request(method, { ...payload });
}
index.test.ts:
import { dispatch } from './';
import jayson, { HttpsClient } from 'jayson/promise';
import { mocked } from 'ts-jest';
jest.mock('jayson/promise');
const httpsClientMock = mocked(jayson.Client.https);
describe('65924278', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should pass', async () => {
const url = 'http://example.com:8000';
const method = '';
const payload = {};
const serviceMock = ({
request: jest.fn(),
} as unknown) as HttpsClient;
httpsClientMock.mockReturnValueOnce(serviceMock);
await dispatch(url, method, payload);
expect(jayson.Client.https).toBeCalledTimes(1);
expect(serviceMock.request).toBeCalledWith('', {});
});
});
unit test result:
PASS examples/65924278/index.test.ts (10.748 s)
65924278
√ should pass (13 ms)
----------|---------|----------|---------|---------|-------------------
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: 13.15 s
Related
I use the simple-git package. I have the following function:
import simpleGit from 'simple-git';
/**
* The function returns the ticket Id, is presents, in the branch name
* #returns ticket Id
*/
export const getTicketIdFromBranchName = async (ticketRegex: RegExp) => {
const git = simpleGit();
try {
const localBranches = await git.branchLocal();
const currentBranch = localBranches.current;
const currentBranchTicketMatches = currentBranch.match(ticketRegex);
if (currentBranchTicketMatches) {
return currentBranchTicketMatches[0];
}
return null;
} catch {
return null;
}
};
I try to create a unit-test for this function:
import { getTicketIdFromBranchName } from '#/utils/git-info';
const TICKET_ID_REGEX = /((?<!([A-Z]{1,10})-?)[A-Z]+-\d+)/.source;
describe('[utils/git-info]', () => {
it('getTicketIdFromBranchName | Function should proper ticket Id when there is one', async () => {
const ticketId = 'CLO-1234';
jest.mock('simple-git', () => {
const mGit = {
branchLocal: jest.fn(() => Promise.resolve({ current: `${ticketId} DUMMY TEST` })),
};
return jest.fn(() => mGit);
});
const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX));
expect(result === ticketId).toEqual(true);
});
});
But the unit-test fails. I says I expected to get true but it got false in the final line.
I guess I use jest.mock in the wrong way.
The official documentation has a key description of the use of jest.mock.
Note: In order to mock properly, Jest needs jest.mock('moduleName') to be in the same scope as the require/import statement.
You call the jest.mock('moduleName') inside test case function scope, but import the git-info module in module scope. That's why mock doesn't work.
Use require('moduleName') or await import('moduleName') in test case function. The order of the require/import and jest.mock() statements does not matter.
git-info.js:
import simpleGit from 'simple-git';
/**
* The function returns the ticket Id, is presents, in the branch name
* #returns ticket Id
*/
export const getTicketIdFromBranchName = async (ticketRegex) => {
const git = simpleGit();
try {
const localBranches = await git.branchLocal();
const currentBranch = localBranches.current;
const currentBranchTicketMatches = currentBranch.match(ticketRegex);
if (currentBranchTicketMatches) {
return currentBranchTicketMatches[0];
}
return null;
} catch {
return null;
}
};
git-info.test.js:
const TICKET_ID_REGEX = /((?<!([A-Z]{1,10})-?)[A-Z]+-\d+)/.source;
describe('[utils/git-info]', () => {
it('getTicketIdFromBranchName | Function should proper ticket Id when there is one', async () => {
const { getTicketIdFromBranchName } = await import('./git-info');
const ticketId = 'CLO-1234';
jest.mock(
'simple-git',
() => {
const mGit = {
branchLocal: jest.fn(() => Promise.resolve({ current: `${ticketId} DUMMY TEST` })),
};
return jest.fn(() => mGit);
},
{ virtual: true }
);
const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX));
expect(result === ticketId).toEqual(true);
});
});
Test result:
PASS stackoverflow/71808909/git-info.test.js (7.439 s)
[utils/git-info]
✓ getTicketIdFromBranchName | Function should proper ticket Id when there is one (6892 ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 84.62 | 50 | 100 | 81.82 |
git-info.js | 84.62 | 50 | 100 | 81.82 | 19-21
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.215 s, estimated 9 s
package version: "jest": "^26.6.3"
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 |
----------|---------|----------|---------|---------|-------------------
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
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
I have the following simplified method and test, where I want to test that handleResponse() has been called.
The test fails with handleResponse() not beeing called at all. If I modify the code to run handleResponse outside the promise, the test completes successfully.
Is this because the promise is async, and the assertion is being run before the promise? If so, how can I wait for the promise to the completed before running the assertion?
Code:
export function fetchList(id) {
// handleResponse(response); // Works here
return (dispatch) => {
service.getList(id).then((response) => {
handleResponse(response); // Doesnt work here
}, (error) => {
addNotification(error);
});
};
}
Test:
describe('fetchList', () => {
let getListStub;
let handleResponseStub;
beforeEach(() => {
getListStub = sinon.stub(service, 'getList');
handleResponseStub = sinon.stub(appActions, 'handleResponse');
});
afterEach(() => {
getListStub.restore();
handleResponseStub.restore();
});
it('should dispatch handleResponse on success', () => {
const dispatchStub = sinon.stub();
const id = 1;
const returnValue = 'return value';
getListStub.withArgs(id).resolves(returnValue);
listActions.fetchList(id)(dispatchStub);
sinon.assert.calledOnce(getListStub);
sinon.assert.calledOnce(handleResponseStub); // Fails
});
});
You forget add async/await to your unit test and change to return service.getList(...). Otherwise, the assertion will execute before resolving the promise of service.getList(...).
listActions.ts:
import * as service from "./service";
import { handleResponse, addNotification } from "./appActions";
export function fetchList(id) {
return dispatch => {
return service.getList(id).then(
response => {
handleResponse(response);
},
error => {
addNotification(error);
}
);
};
}
appActions.ts:
export function handleResponse(res) {
console.log("handleResponse");
}
export function addNotification(error) {
console.log("addNotification");
}
service.ts:
export function getList(id) {
return new Promise(resolve => {
setTimeout(() => {
resolve([]);
}, 5000);
});
}
listActions.spec.ts:
import * as service from "./service";
import * as appActions from "./appActions";
import * as listActions from "./listActions";
import sinon from "sinon";
describe("fetchList", () => {
let getListStub;
let handleResponseStub;
beforeEach(() => {
getListStub = sinon.stub(service, "getList");
handleResponseStub = sinon.stub(appActions, "handleResponse");
});
afterEach(() => {
getListStub.restore();
handleResponseStub.restore();
});
it("should dispatch handleResponse on success", async () => {
const dispatchStub = sinon.stub();
const id = 1;
const returnValue = "return value";
getListStub.withArgs(id).resolves(returnValue);
await listActions.fetchList(id)(dispatchStub);
sinon.assert.calledOnce(getListStub);
sinon.assert.notCalled(dispatchStub);
sinon.assert.calledOnce(handleResponseStub); // Fails
});
});
Unit test result with coverage report:
fetchList
✓ should dispatch handleResponse on success
1 passing (95ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 83.33 | 100 | 53.85 | 82.86 | |
appActions.ts | 50 | 100 | 0 | 50 | 2,6 |
listActions.spec.ts | 100 | 100 | 100 | 100 | |
listActions.ts | 85.71 | 100 | 75 | 85.71 | 11 |
service.ts | 25 | 100 | 0 | 25 | 2,3,4 |
---------------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/43186634