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 |
----------|---------|----------|---------|---------|-------------------
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 new to jest, sorry if this is a trivial question but I went through the official jest docs and I am not able to find a solution to the problem.
I am developing a very simple app in nodejs that consumes data from a websocket and propagates it downstream to a set of consumers via zeromq.
The code is the following:
app.js:
const initializer = require("./dependencyInitializer");
const sock = initializer.zmqSock();
const ws = initializer.wsClient();
ws.on('update', data => {
sock.send([data.topic, JSON.stringify(data)]);
});
The websocket client is a class from a third party library extending from EventEmitter.
I would like to create a test asserting that the sock.send function is called exactly once inside the handler of the 'update' event.
This is my approach:
app.spec.js:
const ws = require("./app");
const initializer = require("./dependencyInitializer");
jest.mock("./dependencyInitializer", () => {
return {
wsClient: jest.fn(() => {
const EventEmitter = require("events")
const emitter = new EventEmitter()
return emitter;
}),
zmqSock: jest.fn(() => {
return {
send: jest.fn()
}
})
}
});
describe('on message received from websocket',() => {
it('should pass it to zmq', () => {
const data = {result: "ok"};
expect(initializer.wsClient).toHaveBeenCalledTimes(1);
expect(initializer.zmqSock).toHaveBeenCalledTimes(1);
const _sock = initializer.zmqSock();
const _ws = initializer.wsClient();
_ws.emit("update", data);
expect(_sock.send).toHaveBeenCalledTimes(1);
});
});
The test fails with the following:
on message received from websocket › should pass it to zmq
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
28 | const _ws = initializer.wsClient();
29 | _ws.emit("update", data);
> 30 | expect(_sock.send).toHaveBeenCalledTimes(1);
| ^
31 | });
32 | });
I am not sure if I am on the right path, I would like to understand what would be the best approach to develop a test like this.
Thanks
After mocking by jest.mock(), when the .wsClient() and .zmqSock() methods are called in the app.js file and app.spec.js file, the sock and ws objects in app.js are different with the app.spec.js.
{
wsClient: jest.fn(() => {
const EventEmitter = require("events")
const emitter = new EventEmitter()
return emitter;
})
}
Every time you call .wsClient(), it will create a new object.
Emitter can only listen for events from its own emit. The solution is to create the mock emitter and sock objects in the mock factory.
app.js:
const initializer = require('./dependencyInitializer');
const sock = initializer.zmqSock();
const ws = initializer.wsClient();
ws.on('update', (data) => {
sock.send([data.topic, JSON.stringify(data)]);
});
module.exports = { sock, ws };
app.test.js:
const app = require('./app');
const initializer = require('./dependencyInitializer');
jest.mock(
'./dependencyInitializer',
() => {
const EventEmitter = require('events');
const emitter = new EventEmitter();
const mSock = { send: jest.fn() };
return {
wsClient: jest.fn(() => emitter),
zmqSock: jest.fn(() => mSock),
};
},
{ virtual: true }
);
describe('on message received from websocket', () => {
it('should pass it to zmq', () => {
const data = { result: 'ok' };
expect(initializer.wsClient).toHaveBeenCalledTimes(1);
expect(initializer.zmqSock).toHaveBeenCalledTimes(1);
const _sock = initializer.zmqSock();
const _ws = initializer.wsClient();
// check if they have same reference
expect(app.sock).toBe(_sock);
expect(app.ws).toBe(_ws);
_ws.emit('update', data);
expect(_sock.send).toHaveBeenCalledTimes(1);
});
});
test result:
PASS examples/70024105/app.test.js (9.279 s)
on message received from websocket
✓ should pass it to zmq (2 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
app.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.112 s
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 |
----------|---------|----------|---------|---------|-------------------
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
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
-------------------|---------|----------|---------|---------|-------------------