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
Related
How do I individually implement stubs to functions invoked inside a parent function?
Assuming I have these functions (req1,req2...) that are HTTP Requests from external services that are all returning differing values, is there a way where I can apply stubs for req1 or req2 individually to mock their values?
The purpose of this is because I need to do this to test a function that relies on an OTP verification and I want to bypass said verification in order to cover all branches in my testing.
import request from 'request-promise'
const request1 = async (data) => return request({uri: "service1.com/get", method: "GET"})
const apiRequests = async (data) => {
const req1 = await request1(data); // I want to mock this value to false
const req2 = await request2(data); // I want to mock this value to true
if (req1 && req2) {
const req3 = await request3(data);
const req4 = await request4(data);
return "Second return"
}
return "First return"
}
I've always been overwhelmed whenever trying to understand the deeper levels of mocking and most of the examples I see online aren't as nested the problem I'm facing so I'm a bit puzzled about how to go on about this.
I also work in a pretty strict setup so I'm not really allowed to use any other libraries/packages outside of Loopback's built-in testing libraries.
You can use stub.onCall(n) API.
Defines the behavior of the stub on the nth call. Useful for testing sequential interactions.
Besides, sinon does NOT support stub a standalone function import from a package, you need to use link seams, so that we use proxyquire package to construct seams.
E.g.
apiRequest.ts:
import request from 'request-promise';
const request1 = async (data) => request({ uri: 'service1.com/get', method: 'GET' });
export const apiRequests = async (data) => {
const req1 = await request1(data);
const req2 = await request1(data);
console.log(req1, req2);
if (req1 && req2) {
const req3 = await request1(data);
const req4 = await request1(data);
return 'Second return';
}
return 'First return';
};
apiRequest.test.ts
import proxyquire from 'proxyquire';
import sinon from 'sinon';
describe('70241641', () => {
it('should second return', async () => {
const rpStub = sinon.stub().onCall(0).resolves(true).onCall(1).resolves(true);
const { apiRequests } = proxyquire('./apiRequest', {
'request-promise': rpStub,
});
const actual = await apiRequests('test data');
sinon.assert.match(actual, 'Second return');
});
it('should first second', async () => {
const rpStub = sinon.stub().onCall(0).resolves(false).onCall(1).resolves(true);
const { apiRequests } = proxyquire('./apiRequest', {
'request-promise': rpStub,
});
const actual = await apiRequests('test data');
sinon.assert.match(actual, 'First return');
});
});
test result:
70241641
true true
✓ should second return (2374ms)
false true
✓ should first second
2 passing (2s)
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
apiRequest.ts | 100 | 100 | 100 | 100 |
---------------|---------|----------|---------|---------|-------------------
I am trying to track the number of calls to another function using a test case written with Sinon. I have the following code, and on running the test, the calledFuncSpy callCount always gets me 0, which is not expected. Meanwhile, running another spy on console.log gives me the correct output, and I have left it commented in the code. Does anyone know what could be the problem?
Here’s the code. Let me know if more information is required to solve this problem.
specs/mymodule.js
const mymodule = require("../src/mymodule.js");
var sinon = require("sinon");
const assert = require("assert");
describe("Test suite", function() {
let calledFuncSpy;
let consoleSpy;
beforeEach(function () {
calledFuncSpy = sinon.spy(mymodule, "calledFunc");
consoleSpy = sinon.spy(console, "log");
})
afterEach(function () {
calledFuncSpy.restore();
consoleSpy.restore();
})
it("Test the add method", function () {
mymodule.ownHelloWorld();
assert.deepStrictEqual(calledFuncSpy.callCount, 1);
// assert.deepStrictEqual(consoleSpy.callCount, 1);
});
})
src/mymodule.js
function ownHelloWorld() {
calledFunc();
}
function calledFunc() {
console.log("Hello world");
}
module.exports = {
ownHelloWorld,
calledFunc
}
calledFunc called inside ownHelloWorld function can NOT be stubbed. It will always use the original calledFunc function. sinon.spy(mymodule, "calledFunc") can NOT modify the reference of it when calling the ownHelloWorld function. Also, see this comment
You can refactor it like this:
mymodule.js:
function ownHelloWorld() {
module.exports.calledFunc();
}
function calledFunc() {
console.log('Hello world');
}
module.exports = {
ownHelloWorld,
calledFunc,
};
mymodule.test.js:
const mymodule = require('./mymodule');
var sinon = require('sinon');
describe('Test suite', function () {
let calledFuncSpy;
let consoleSpy;
beforeEach(function () {
calledFuncSpy = sinon.spy(mymodule, 'calledFunc');
consoleSpy = sinon.spy(console, 'log');
});
afterEach(function () {
calledFuncSpy.restore();
consoleSpy.restore();
});
it('Test the add method', function () {
mymodule.ownHelloWorld();
sinon.assert.calledOnce(calledFuncSpy);
});
});
test result:
Test suite
Hello world
✓ Test the add method
1 passing (5ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
mymodule.js | 100 | 100 | 100 | 100 |
-------------|---------|----------|---------|---------|-------------------
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 |
----------|---------|----------|---------|---------|-------------------
I'm writing a unit test for a function that uses node-cache.
At the following function,
I want to have a string return at the first cache.get
an array in the second cache.get
Please note that I removed some parts of code from testFunction since it is not relavant to my question.
const NodeCache = require('node-cache');
const cache = new NodeCache();
...
const testFunction = () => {
let myStringCache = cache.get('cacheName1');
let myArrayCache = cache.get('cacheName2');
... Do something with caches ...
return 'return something';
}
...
module.exports = {
...,
testFunction,
...
}
I created the following test
describe('sample test with testFunction() ', ()=>{
let stubCache;
let stub;
before((done)=>{
sandbox = sinon.createSandbox();
stubCache = sandbox.stub(cache, 'get');
stubCache.withArgs('cacheName1').returns('sample string');
stubCache.withArgs('cacheName2').returns([1,2,3,4,5,6]);
stub = proxyquire('./filelocation.js',{
'node-cache': stubCache
});
done();
});
it('should not throw error',(done)=>{
chai.expect(stub.testFunction()).not.to.throw;
});
})
I was Googling around, and there is some partial solution to use proxyquire to stub the value. but looks like it does stub but it's not where I wanted.
It stubs at NodeCache but cache
So I have questions:
Does anybody know how to stub cache.get() with mocha, chai or sinon? If so, please share how you do it ?
Is it possible to stub different returns by the argument of cache.get()?
Here is the unit test solution:
index.js:
const NodeCache = require("node-cache");
const cache = new NodeCache();
const testFunction = () => {
let myStringCache = cache.get("cacheName1");
let myArrayCache = cache.get("cacheName2");
console.log("myStringCache:", myStringCache);
console.log("myArrayCache:", myArrayCache);
return "return something";
};
module.exports = {
testFunction
};
index.spec.js:
const sinon = require("sinon");
const proxyquire = require("proxyquire");
const chai = require("chai");
describe("sample test with testFunction() ", () => {
let stubCache;
let stub;
let getCacheStub;
before(() => {
sandbox = sinon.createSandbox();
getCacheStub = sandbox.stub();
stubCache = sandbox.stub().callsFake(() => {
return {
get: getCacheStub
};
});
getCacheStub.withArgs("cacheName1").returns("sample string");
getCacheStub.withArgs("cacheName2").returns([1, 2, 3, 4, 5, 6]);
stub = proxyquire("./", {
"node-cache": stubCache
});
});
it("should not throw error", () => {
const logSpy = sinon.spy(console, "log");
chai.expect(stub.testFunction()).not.to.throw;
sinon.assert.calledWith(
logSpy.firstCall,
"myStringCache:",
"sample string"
);
sinon.assert.calledWith(logSpy.secondCall, "myArrayCache:", [
1,
2,
3,
4,
5,
6
]);
});
});
Unit test result with 100% coverage:
sample test with testFunction()
myStringCache: sample string
myArrayCache: [ 1, 2, 3, 4, 5, 6 ]
✓ should not throw error
1 passing (87ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
index.spec.js | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/58278211
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
-------------------|---------|----------|---------|---------|-------------------