I am trying to stub a call to the aws parameter store (PS). But even though I added the stub in multiple ways it always makes the actual call to aws PS.
the method I am trying to test
function getParamsFromParamterStore() {
return ssm.getParametersByPath(query).promise();
}
One of the stub method I tried
var ssm = new AWS.SSM();
stub1 = sinon.stub(ssm, 'getParametersByPath').returns({promise: () => {}});
moduleName.__get__('getParamsFromParamterStore')();
But this actually makes the call to PS.
Note: since this is a private function (not exported) I am using rewire to access it.
Here is the unit test solution:
index.js:
const AWS = require('aws-sdk');
const ssm = new AWS.SSM();
function getParamsFromParamterStore(query) {
return ssm.getParametersByPath(query).promise();
}
index.test.js:
const rewire = require('rewire');
const sinon = require('sinon');
const { expect } = require('chai');
const mod = rewire('./');
describe('60447015', () => {
it('should pass', async () => {
const ssmMock = { getParametersByPath: sinon.stub().returnsThis(), promise: sinon.stub().resolves('mock data') };
const awsMock = {
SSM: ssmMock,
};
mod.__set__('ssm', awsMock.SSM);
const actual = await mod.__get__('getParamsFromParamterStore')('query');
expect(actual).to.be.eq('mock data');
sinon.assert.calledWithExactly(ssmMock.getParametersByPath, 'query');
sinon.assert.calledOnce(ssmMock.promise);
});
});
Unit test results with 100% coverage:
60447015
✓ should pass
1 passing (30ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Related
I'm trying to mock the crypto module of nodejs with the jestjs framework.
Here is the piece of code that I want to mock:
app.js:
const hash = crypto
.createHmac('sha256', API_TOKEN)
.update(JSON.stringify(body))
.digest('hex');
if(hash === signature) {
//verified successfully. Implement next logic
}
Here, I want the .digest function to give the value contained in the signature variable.
Following is the mock code:
jest.mock('crypto', () => {
return {
createHmac: jest.fn(() => ({
update: jest.fn(),
digest: jest.fn(() => '123') //here '123' is placeholder for 'signature value
}))
};
});
However, when running the test, the main files throws an error like this:
TypeError: Cannot read property 'update' of undefined
What am I missing here for jest mock?
You should use mockFn.mockReturnThis() to obtain method chain call.
E.g.
app.js:
const crypto = require('crypto');
function main() {
const API_TOKEN = 'API_TOKEN';
const signature = '123';
const body = {};
const hash = crypto
.createHmac('sha256', API_TOKEN)
.update(JSON.stringify(body))
.digest('hex');
if (hash === signature) {
console.log('verified successfully. Implement next logic');
}
}
module.exports = main;
app.spec.js:
const main = require('./app');
const crypto = require('crypto');
jest.mock('crypto', () => {
return {
createHmac: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnThis(),
digest: jest.fn(() => '123'),
};
});
describe('64386858', () => {
it('should pass', () => {
const logSpy = jest.spyOn(console, 'log');
main();
expect(crypto.createHmac).toBeCalledWith('sha256', 'API_TOKEN');
expect(crypto.update).toBeCalledWith(JSON.stringify({}));
expect(crypto.digest).toBeCalledWith('hex');
expect(logSpy).toBeCalledWith('verified successfully. Implement next logic');
logSpy.mockRestore();
});
});
unit test result:
PASS src/stackoverflow/64386858/app.spec.js
64386858
✓ should pass (17ms)
console.log node_modules/jest-environment-jsdom/node_modules/jest-mock/build/index.js:866
verified successfully. Implement next logic
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 50 | 100 | 100 | |
app.js | 100 | 50 | 100 | 100 | 13 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.087s, estimated 11s
I have a Module a
const b = require(./b);
function aGetResult() {
return b.getInfo();
}
Module B
const c = require(./c);
function getInfo() {
return getDetailInfo();
}
function getDetailInfo() {
const result = c.getApiResult();
return result
}
Module C
function getApiResult() {
return api.get(/test/1);
}
I've written a test for module A but am running into an issue with stubbing dependencies.I just want to stub c.getApiResult() and not b.getInfo() or b.getDetailInfo(). I've tried selectively stubbing using proxyquire but am having issues. Any help?
You should use Globally override require of proxyquire package.
a depends b, b depends on c. Now you want to mock the indirect c dependency instead of direct b dependency when you test a. It's NOT recommended to do this. But anyway, here is the solution:
E.g.
a.js:
const b = require('./b');
function aGetResult() {
return b.getInfo();
}
exports.aGetResult = aGetResult;
b.js:
const c = require('./c');
function getInfo() {
return getDetailInfo();
}
function getDetailInfo() {
const result = c.getApiResult();
return result;
}
module.exports = { getInfo };
c.js:
const api = {
get(url) {
return 'real result';
},
};
function getApiResult() {
return api.get('/test/1');
}
module.exports = { getApiResult };
a.test.js:
const proxyquire = require('proxyquire');
const { expect } = require('chai');
const sinon = require('sinon');
describe('63275147', () => {
it('should pass', () => {
const stubs = {
'./c': {
getApiResult: sinon.stub().returns('stubbed result'),
'#global': true,
},
};
const a = proxyquire('./a', stubs);
const actual = a.aGetResult();
expect(actual).to.be.eq('stubbed result');
sinon.assert.calledOnce(stubs['./c'].getApiResult);
});
});
unit test result:
63275147
✓ should pass (2630ms)
1 passing (3s)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 83.33 | 100 | 60 | 83.33 |
a.js | 100 | 100 | 100 | 100 |
b.js | 100 | 100 | 100 | 100 |
c.js | 50 | 100 | 0 | 50 | 3-8
----------|---------|----------|---------|---------|-------------------
I'm using the package aws-param-store and I'm trying to write Unit tests
that stub calls to getParametersByPath().
Package can be found here:
https://www.npmjs.com/package/aws-param-store
Here is my sinon code to stub the call. The function getParametersByPath is an async function
so I'm trying to return a resolved promise to stub it:
const awsParameterStore = require('aws-param-store');
const sinon = require("sinon");
let sandbox = sinon.createSandbox();
// In My Test:
let parms = new Map();
parms.set("key1","value1");
parms.set("key2","value2");
sandbox.stub(awsParameterStore,'getParametersByPath').callsFake(async function(prefix){
console.log("INSIDE STUB for getParametersByPath:" + prefix);
return Promise.resolve(parms);
});
My app makes a call to the function like this:
let parameters = await awsParameterStore.getParametersByPath("/foo");
However, instead of getting back the Map of dummy parameters, I get an empty object {}.
I can see that the stub is getting called.
Any ideas on how to properly stub this so I can return some dummy parameters in my unit tests?
Thanks!
I tried to check your code, and it's completely fine.
import test from 'ava'
const awsParameterStore = require('aws-param-store');
const sinon = require('sinon')
test('test stub', async t => {
let parms = new Map();
parms.set("key1","value1");
parms.set("key2","value2");
parms.set("key3","value3");
parms.set("key4","value4");
sinon.stub(awsParameterStore, 'getParametersByPath').callsFake(async function(prefix){
console.log("INSIDE STUB for getParametersByPath:" + prefix)
return Promise.resolve(parms);
})
const res = await awsParameterStore.getParametersByPath('/some-prefix')
console.log(JSON.stringify(res))
res.forEach((value, key) => {
console.log(`[${key}]= ${value}`)
})
t.true(true)
})
What is weir:
console.log - show that result is {}
but if you debug or log the value one by one, you can see that stub works fine:
Debugger attached.
INSIDE STUB for getParametersByPath:/some-prefix
{}
[key1]= value1
[key2]= value2
[key3]= value3
[key4]= value4
✔ test stub
UPD: problem is how to log Map by JSON.stringify(). So, you must log the map as:
console.log(JSON.stringify([...res]))
Here is the unit test solution:
index.js:
const awsParameterStore = require('aws-param-store');
async function main() {
let parameters = await awsParameterStore.getParametersByPath('/foo');
return parameters;
}
module.exports = main;
index.test.js:
const main = require('./');
const sinon = require('sinon');
const awsParameterStore = require('aws-param-store');
const { expect } = require('chai');
let sandbox = sinon.createSandbox();
describe('59787603', () => {
it('should pass', async () => {
let parms = new Map();
parms.set('key1', 'value1');
parms.set('key2', 'value2');
sandbox.stub(awsParameterStore, 'getParametersByPath').resolves(parms);
const actual = await main();
expect(actual).to.be.eql(parms);
});
});
Unit test results with 100% coverage:
59787603
✓ should pass
1 passing (11ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/59787603
My unit test failing for following code.when I run test i see below result with unknown error.I am not getting how to test second parameter paylod in console.log method.
Test console log()
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 66.67 | 100 | 0 | 66.67 | |
logger.js | 66.67 | 100 | 0 | 66.67 | 6 |
-----------|----------|----------|----------|----------|-------------------|
npm ERR! Test failed. See above for more details.
//logger.js
'use strict';
const log = (message, payload) => {
console.log(message, JSON.stringify(payload, null, 2));
};
module.exports = { log };
// log.test.js
'use strict';
const chai = require('chai');
const sinon = require('sinon');
chai.use(require('sinon-chai'));
const { expect } = chai;
const log = require('../../src/logger');
describe('Test console log()', () => {
it('should log value in console', () => {
this.stub = sinon.stub(console, 'log');
log('test invoked', { option: 1 });
});
});
Looks like you just need to destructure your require for logger.js:
'use strict';
const sinon = require('sinon');
const { log } = require('../../src/logger'); // <= destructuring assignment
describe('Test console log()', () => {
it('should log value in console', () => {
const stub = sinon.stub(console, 'log');
log('test invoked', { option: 1 });
stub.restore();
sinon.assert.calledWith(stub, 'test invoked', JSON.stringify({ option: 1 }, null, 2)); // Success!
});
});
What would be the proper way to unit test this getall function using mocha/chai? I'm having a hard time understanding what to expect with Promise.all.
const Promise = require('bluebird');
const someApiService = require('./someapiservice');
const _ = require('underscore');
function getall(arr) {
let promises = _.map(arr, function(item) {
return someApiService(item.id);
});
return Promise.all(promises);
}
We should stub standalone function someApiService use link seams. This is the CommonJS version, so we will be using proxyquire to construct our seams. Additionally, I use sinon.js to create the stub for it.
E.g.
getall.js:
const Promise = require('bluebird');
const someApiService = require('./someapiservice');
const _ = require('underscore');
function getall(arr) {
let promises = _.map(arr, function (item) {
return someApiService(item.id);
});
return Promise.all(promises);
}
module.exports = getall;
someapiservice.js:
module.exports = function someApiService(id) {
return Promise.resolve('real data');
};
getall.test.js:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const { expect } = require('chai');
describe('45337461', () => {
it('should pass', async () => {
const someapiserviceStub = sinon.stub().callsFake((id) => {
return Promise.resolve(`fake data with id: ${id}`);
});
const getall = proxyquire('./getall', {
'./someapiservice': someapiserviceStub,
});
const actual = await getall([{ id: 1 }, { id: 2 }, { id: 3 }]);
expect(actual).to.deep.equal(['fake data with id: 1', 'fake data with id: 2', 'fake data with id: 3']);
sinon.assert.calledThrice(someapiserviceStub);
});
});
unit test result:
45337461
✓ should pass (2745ms)
1 passing (3s)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 88.89 | 100 | 66.67 | 88.89 |
getall.js | 100 | 100 | 100 | 100 |
someapiservice.js | 50 | 100 | 0 | 50 | 2
-------------------|---------|----------|---------|---------|-------------------