Sinon stub for node-postgres client - node.js

I'm writing a Node application in TypeScript which is querying a Postgres database. In the unit tests I'm unable to mock the query-function.
My unit code looks like this:
class MyClass {
/* more stuff */
async validate(event: any): Promise<boolean> {
const client = new Client();
await client.connect();
const result = await client.query(`SELECT * FROM "user" WHERE "user_id" = '${user_id}'`);
console.log(
result
);
/* more logic */
}
}
My unit test looks like this:
describe("...", () => {
sinon.stub(pg.Client.prototype, "connect").resolves();
let stub = sinon.stub(pg.Client.prototype, "query").withArgs(
`SELECT * FROM "user" WHERE "user_id" = '1234'`,
sinon.match.any,
sinon.match.any
).resolves("asdf");
it("...", () => {
await myClassInstance.validate(event);
});
});
Not talking about the test expectations for now, what I would expect is "asdf" printed on the console. But I don't!
I've tried removing the .withArgs(...) completely and then I can see "asdf". (But obviously it doesn't help that much ...)
What I've also tried is printing stub.lastCall and I see
args: [ `SELECT * FROM "user" WHERE "user_id" = 'user_with_verified_subscription'` ]
somewhere then ...
Removing the two sinon.match.any matchers don't help either because then the TS compiler is complaining that Client.query takes 3 arguments ...
Any help is highly appreciated!
Best regards,
Max

Here is the unit test solution using proxyquire and sinon:
index.ts:
import { Client } from 'pg';
class MyClass {
async validate(event: any) {
const user_id = 1;
const client = new Client();
await client.connect();
const result = await client.query(`SELECT * FROM "user" WHERE "user_id" = '${user_id}'`);
console.log(result);
}
}
export default MyClass;
index.test.ts:
import sinon from 'sinon';
import proxyquire from 'proxyquire';
describe('60172091', () => {
it('should valdiate', async () => {
const pgClientStub = {
connect: sinon.stub().returnsThis(),
query: sinon
.stub()
.withArgs(`SELECT * FROM "user" WHERE "user_id" = '1'`)
.resolves('asdf'),
};
const pgStub = sinon.stub().callsFake(() => pgClientStub);
const MyClass = proxyquire('./index', {
pg: { Client: pgStub },
}).default;
const logSpy = sinon.spy(console, 'log');
const instance = new MyClass();
await instance.validate();
sinon.assert.calledOnce(pgStub);
sinon.assert.calledOnce(pgClientStub.connect);
sinon.assert.calledWithExactly(pgClientStub.query, `SELECT * FROM "user" WHERE "user_id" = '1'`);
sinon.assert.calledWithExactly(logSpy, 'asdf');
});
});
Unit test results with 100% coverage:
60172091
asdf
✓ should valdiate (2212ms)
1 passing (2s)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------

Related

Node.js BigQuery unit testing

I'm new to writing mocha unit tests and would like to ask how the unit test for the following code would look like (especially for the BigQuery part). The code is in a PubSub triggered Cloud Function and inserts rows into a BigQuery table:
/**
* Triggered from a message on a Cloud Pub/Sub topic.
*
* #param {!Object} event Event payload.
* #param {!Object} context Metadata for the event.
*/
const {BigQuery} = require('#google-cloud/bigquery');
const bigqueryClient = new BigQuery();
const dataset = 'dataset_name';
const table = 'table_name';
exports.sendtobigquery = (event, context) => {
const pubsubMessage = Buffer.from(event.data, 'base64').toString();
BigQueryInsert(pubsubMessage, dataset, table);
};
async function BigQueryInsert(pubsubMessage, dataset, table) {
const date = new Date().toISOString(); // new date
const rows = [{field1: pubsubMessage, field2: date}]; // new field2
await bigqueryClient
.dataset(dataset)
.table(table)
.insert(rows);
}
I've seen here Mocking ES6 BigQuery class that sinon and proxyquire should be used but I don't understand how to do that for my code exactly.
Any help is appreciated.
Update:
I added a new date field as part of the row but I'm having trouble here too when it comes to the unit test. For the date I tried using sinon.useFakeTimers in the index.test.js from the answer like that
describe('66267929', () => {
const now = new Date();
const date = sinon.useFakeTimers(now.getTime());
beforeEach(() => {
sinon.restore();
});
afterEach(() => {
sinon.restore();
});
it('should pass', async () => {
... // same code
... // same code
sinon.assert.calledWithExactly(bigqueryClientStub.insert, [{ field1: 'teresa teng', field2: date }]);
});
});
but that resulted in "AssertError: expected stub to be called once but was called 0 times". How can this be done?
Since you call the BigQueryInsert function without async/await, we need to flush the promise queue to ensure that all asynchronous method calls have been completed on the bigqueryClient object.
We use proxyquire and sinonjs to stub the BigQuery constructor.
We use returnsThis() to achieve the chain methods call.
E.g.
index.js:
const { BigQuery } = require('#google-cloud/bigquery');
const bigqueryClient = new BigQuery();
const dataset = 'dataset_name';
const table = 'table_name';
exports.sendtobigquery = (event, context) => {
const pubsubMessage = Buffer.from(event.data, 'base64').toString();
BigQueryInsert(pubsubMessage, dataset, table);
};
async function BigQueryInsert(pubsubMessage, dataset, table) {
const rows = [{ field1: pubsubMessage }];
await bigqueryClient.dataset(dataset).table(table).insert(rows);
}
index.test.js:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));
describe('66267929', () => {
afterEach(() => {
sinon.restore();
});
it('should pass', async () => {
const bigqueryClientStub = {
dataset: sinon.stub().returnsThis(),
table: sinon.stub().returnsThis(),
insert: sinon.stub().resolves(),
};
const googleCloundBigqueryStub = {
BigQuery: sinon.stub().returns(bigqueryClientStub),
};
const { sendtobigquery } = proxyquire('./', {
'#google-cloud/bigquery': googleCloundBigqueryStub,
});
const data = Buffer.from('teresa teng').toString('base64');
sendtobigquery({ data });
await flushPromises();
sinon.assert.calledOnce(googleCloundBigqueryStub.BigQuery);
sinon.assert.calledWithExactly(bigqueryClientStub.dataset, 'dataset_name');
sinon.assert.calledWithExactly(bigqueryClientStub.table, 'table_name');
sinon.assert.calledWithExactly(bigqueryClientStub.insert, [{ field1: 'teresa teng' }]);
});
});
unit test result:
66267929
✓ should pass (343ms)
1 passing (346ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------

How to use sinon to stub aws-param-store getParametersByPath()

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

How to stub node-cache's cache.get()?

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

Google Cloud Datastore jest nodejs node_modules

I am trying to unit test the below listEntities function by mocking runQuery and createQuery functions. Maybe I should just give up and do an integration test with an emulator. Anyway, here is my code
Implementation:
const Datastore = require('#google-cloud/datastore');
const datastore = Datastore();
const runQueryDS = (query) => datastore.runQuery(query);
const createQueryDS = (kind) => datastore.createQuery(kind);
export const listEntities = (kind, runQuery = runQueryDS, createQuery = createQueryDS) => {
console.log('listEntities');
const query = createQuery(kind);
runQuery(query).then((results) => results[0]);
};
Test:
import { listEntities } from './datastore.api';
describe('datastore api', () => {
describe('listEntities', () => {
test('should return list of items', () => {
console.log('begin test');
const kind = 'TestRun';
const createdQuery = 'createdQuery';
const expectedResult = ['returnedFromQuery'];
const returnedFromExecutedQuery = [expectedResult];
const createQuery = jest.fn().mockImplementation(() => (createdQuery));
const runQuery = jest.fn().mockImplementation(() => (returnedFromExecutedQuery));
const result = listEntities(kind, runQuery, createQuery);
expect(result).toEqual(expectedResult);
});
});
});
This is the error I get
FAIL app/datastore.api.test.js
● Test suite failed to run
Cannot find module './datastore_client_config' from 'datastore_client.js'
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:191:17)
at Object.<anonymous> (node_modules/#google-cloud/datastore/src/v1/datastore_client.js:30:18)
Thank you!
Unit testing should be completed first, followed by integration testing. Unit tests are easier to write than integration tests, and have good isolation, do not rely on external services, have no side effects, and can run in different environments.
Here is the unit test solution:
index.js:
const Datastore = require('#google-cloud/datastore');
const datastore = Datastore();
const runQueryDS = (query) => datastore.runQuery(query);
const createQueryDS = (kind) => datastore.createQuery(kind);
const listEntities = (kind, runQuery = runQueryDS, createQuery = createQueryDS) => {
console.log('listEntities');
const query = createQuery(kind);
return runQuery(query).then((results) => results[0]);
};
module.exports = { listEntities };
index.test.js:
const { listEntities } = require('./');
const Datastore = require('#google-cloud/datastore');
jest.mock('#google-cloud/datastore', () => {
const mDatasotre = {
runQuery: jest.fn(),
createQuery: jest.fn(),
};
return jest.fn(() => mDatasotre);
});
describe('47128513', () => {
describe('#listEntities', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should list entities', async () => {
const mDatastore = Datastore();
mDatastore.createQuery.mockReturnValueOnce('fake query');
mDatastore.runQuery.mockResolvedValueOnce([{ id: 1 }]);
const actual = await listEntities('kind');
expect(actual).toEqual({ id: 1 });
expect(mDatastore.createQuery).toBeCalledWith('kind');
expect(mDatastore.runQuery).toBeCalledWith('fake query');
});
});
});
unit test result with coverage report:
PASS src/stackoverflow/47128513/index.test.js (12.111s)
47128513
#listEntities
✓ should list entities (12ms)
console.log src/stackoverflow/47128513/index.js:355
listEntities
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.865s, estimated 15s
source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47128513

Unit test bluebird Promise.all with Mocha/Chai

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
-------------------|---------|----------|---------|---------|-------------------

Resources