I am working on a Node.js application.
This is a code of validate.js file, where I have defined a class Validate and static method validateTicket and at the bottom exported the class.
validate.js file
const request = require("request");
const config = { urlBase: "" };
class Validate {
static validateTicket = (ticket) => {
const options = { url: config.urlBase + ticket, json: true, rejectUnauthorized: false };
return new Promise((resolve, reject) => {
request.get(options, (error, response, body) => {
if (error) {
reject(error);
return;
}
if (response) {
resolve(response);
return;
}
reject(null);
});
});
};
}
module.exports = Validate;
This is the main utility file where I am importing the validate.js file and there are 3 functions validateTicketReqs, to_json, and sendErrorResponse.
validateTicketReqs function do validation on response received from class method.
util.js file
const validate = require('./validate.js');
function validateTicketReqs(req, res, next, flag, num) {
let payload = req.body;
let ticket= (payload.ticket) ? payload.ticket: null;
let error_msg = 'Failed to obtain a successful response '
validate.validateTicket(ticket).then(
(resolution) => {
let json = to_json(resolution); // function will return null value for json
if (json === null || !('body' in json)) {
return sendErrorResponse(req, res, 500, 'error');
}
...
})
function sendErrorResponse(req, res, code, msg) {
let resBody = {
msg: msg
};
res.status(code);
res.send(resBody);
}
test_util.js file
const validate = require('./validate.js');
describe("Check", () => {
it("should fail due to error", (done) => {
let req = {
authData: {'user_id': "mock"},
body: {"num": "mock", "tikcet": "mock"},
request_id: 'mock'
};
let res = {send: sinon.spy(), status: sinon.spy()};
let next = sinon.spy();
mock_buffer = {
toJSON : function() { return {body: {environment: 'mock', status: 'mock'}}},
statusCode: 500
}
const validateTicket= sinon.stub(validate, 'validateTicket')
validateTicket.resolves(mock_buffer)
util.valdateTicketReqs(req, res, next, flag, "mock" );
expect(res.send.calledOnce).to.be.true; ##not getting called
expect(res.send.firstCall.args[0].statusCode).to.equal(500); ##not getting called
util.valdateTicketReqs.restore();
validate.validateTicket.restore();
done();
});
});
The problem I am facing is that (res.send.calledOnce) value is coming as false. I am expecting that res.send() should be called at least once but it is not. Please advise what is the problem here. Let me know if you have a query on the question.
The issue is the expect method is called before the test case finished. The return value of your validate.validateTicket method is a promise, it's asynchronous method. You need return it so that test runner will know if the test case is done.
E.g.
validate.ts:
const request = require("request");
const config = { urlBase: "" };
class Validate {
static validateTicket = (ticket) => {
const options = { url: config.urlBase + ticket, json: true, rejectUnauthorized: false };
return new Promise((resolve, reject) => {
request.get(options, (error, response, body) => {
if (error) {
reject(error);
return;
}
if (response) {
resolve(response);
return;
}
reject(null);
});
});
};
}
module.exports = Validate;
util.ts:
const validate = require("./validate");
function to_json(data) {
return null;
}
function sendErrorResponse(req, res, code, msg) {
const resBody = {
msg: msg,
};
res.status(code);
res.send(resBody);
}
export function validateTicketReqs(req, res, next, flag, num) {
const payload = req.body;
const ticket = payload.ticket ? payload.ticket : null;
const error_msg = "Failed to obtain a successful response ";
return validate.validateTicket(ticket).then((resolution) => {
const json = to_json(resolution);
if (json === null || !("body" in json)) {
return sendErrorResponse(req, res, 500, "error");
}
});
}
util.spec.ts:
import sinon from "sinon";
import { expect } from "chai";
import * as util from "./util";
const validate = require("./validate");
describe("Check", () => {
it("should fail due to error", async () => {
const req = {
authData: { user_id: "mock" },
body: { num: "mock", tikcet: "mock" },
request_id: "mock",
};
const res = {
send: sinon.spy(),
status: sinon.spy(),
};
const next = sinon.spy();
const flag = "";
const mock_buffer = {
toJSON: function() {
return { body: { environment: "mock", status: "mock" } };
},
statusCode: 500,
};
const validateTicketStub = sinon.stub(validate, "validateTicket").resolves(mock_buffer);
await util.validateTicketReqs(req, res, next, flag, "mock");
expect(res.send.calledOnce).to.be.true;
expect(res.status.firstCall.args[0]).to.equal(500);
sinon.assert.calledOnce(validateTicketStub);
validateTicketStub.restore();
});
});
Unit test result with coverage report:
Check
✓ should fail due to error
1 passing (19ms)
--------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files | 76.09 | 30 | 63.64 | 75.56 | |
util.spec.ts | 94.12 | 100 | 66.67 | 93.75 | 22 |
util.ts | 100 | 50 | 100 | 100 | 9,14 |
validate.ts | 37.5 | 0 | 25 | 37.5 |... 12,15,16,17,20 |
--------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/59037819
Related
So I tried to mock a jwt.verify function. below is the function itself and then the mock.
try {
const decodedJwt = new Promise((resolve, reject) => {
jwt.verify(
token,
getPublicKey,
{
algorithms: ['RS256'],
ignoreExpiration: false,
},
(decodeError, decodedValue) => {
if (decodeError) {
reject(decodeError);
}
resolve(decodedValue);
}
);
});
return await decodedJwt;
} catch (error) {
logger.error(error);
return null;
}
The Mock for the verify part
export const jwtVerifyMock = jest.fn();
jest.mock('jsonwebtoken', () => {
return {
decode: jwtDecodeMock,
verify: jwtVerifyMock,
};
});
then this is how I use it in my test
it('decodeJWT should should verify token', async () => {
jwtVerifyMock.mockImplementation(async () => Promise.resolve({ exp: config.exp }));
return decodeJwt(config.jwtToken, true).then(async (value) => {
console.log('payload2', value);
const payload = value as Record<string, unknown>;
expect(payload.exp).toEqual(config.exp);
});
});
But for some reason, it doesn't get resolved. and I get a
Async callback was not invoked within the 60000 ms timeout Error
I tried using mockReturn value but that doesn't work as well. So the next thing I'm guessing is that the resolve function that was passed as a callback to the jwt.verify is where my problem lie.
You should mock the implementation of jwt.verify() and execute the callback function manually with different arguments.
E.g.
decode.ts:
import jwt from 'jsonwebtoken';
export async function decode(token, getPublicKey) {
try {
const decodedJwt = new Promise((resolve, reject) => {
jwt.verify(
token,
getPublicKey,
{
algorithms: ['RS256'],
ignoreExpiration: false,
},
(decodeError, decodedValue) => {
if (decodeError) {
reject(decodeError);
}
resolve(decodedValue);
}
);
});
return await decodedJwt;
} catch (error) {
console.error(error);
return null;
}
}
decode.test.ts:
import { decode } from './decode';
import jwt, { JsonWebTokenError } from 'jsonwebtoken';
describe('66966317', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should decode value', async () => {
const decodedvalue = { name: 'teresa teng' };
const verifySpy = jest.spyOn(jwt, 'verify').mockImplementationOnce((token, getPublicKey, options, callback) => {
callback!(null, decodedvalue);
});
const actual = await decode('teresa teng', true);
expect(actual).toEqual({ name: 'teresa teng' });
expect(verifySpy).toBeCalledWith(
'teresa teng',
true as any,
{ algorithms: ['RS256'], ignoreExpiration: false },
expect.any(Function)
);
});
it('should handle decode error', async () => {
const decodedError = new JsonWebTokenError('network');
const verifySpy = jest.spyOn(jwt, 'verify').mockImplementationOnce((token, getPublicKey, options, callback) => {
callback!(decodedError, undefined);
});
const actual = await decode('teresa teng', true);
expect(actual).toBeNull();
expect(verifySpy).toBeCalledWith(
'teresa teng',
true as any,
{ algorithms: ['RS256'], ignoreExpiration: false },
expect.any(Function)
);
});
});
unit test result:
PASS examples/66966317/decode.test.ts (7.701 s)
66966317
✓ should decode value (3 ms)
✓ should handle decode error (17 ms)
console.error
JsonWebTokenError { name: 'JsonWebTokenError', message: 'network' }
23 | return await decodedJwt;
24 | } catch (error) {
> 25 | console.error(error);
| ^
26 | return null;
27 | }
28 | }
at Object.<anonymous> (examples/66966317/decode.ts:25:13)
at Generator.throw (<anonymous>)
at rejected (examples/66966317/decode.ts:1046:32)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
decode.ts | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.749 s
I am new to Sinon, but I have looked around for a while trying to find an answer for this question..
I have a function I need to test, it returns a promise to call another function will callback..
Below is the function that I need to write test case for:
const bookService = require(./bookService);
const getBook = () => {
const bookName = "book";
const bookID = '111';
return new Promise((resolve, reject) => {
bookService.InfoRequest(bookName, bookID, 'GET', res => {
if(res.error){
reject(res);
}else{
const list = res['allPages'] || [];
if(list = []){
resolve({
pageNumber: 0,
note: "book is no longer exist"
});
}else{
resolve(res['allPages']);
}
}
})
})
}
The bookService.InfoRequest method is not returning anything it returns the callback(res);
I have tried stub the bookService.InfoRequest method, but since it is not returning anything I am not sure how can I modified the callback parameter to test all 3 branchs..
I am using Ava, so I tried something like this:
test('getBook Error Block', t=> {
const stub = sinon.stub(bookService, InfoRequest);
stub.callsFake(() => {
return { error: true };
});
return obj.getBook().then(res => {
t.deepEqual(res, []);
}).catch(error => {
console.log(error.error);
t.deepEqual(error.error, true);
})
})
This is the test cases for the first Branch, the reject(res) branch. There are 2 more very similar only with different callFake.
But the problem is I am not able to print the error out and test shows it passed, but if I change true to false, it also pass...
The stubbed implementation by .callFake() is not correct. The bookService.InfoRequest() method accepts a callback parameter, the res is passed to this callback. So you need to provide a stubbed implementation with this callback function and pass your fake error.
E.g.
bookService.js:
function InfoRequest(bookName, bookId, method, cb) {}
module.exports = { InfoRequest };
obj.js:
const bookService = require('./bookService');
const getBook = () => {
const bookName = 'book';
const bookID = '111';
return new Promise((resolve, reject) => {
bookService.InfoRequest(bookName, bookID, 'GET', (res) => {
if (res.error) {
reject(res);
} else {
const list = res['allPages'] || [];
if ((list = [])) {
resolve({
pageNumber: 0,
note: 'book is no longer exist',
});
} else {
resolve(res['allPages']);
}
}
});
});
};
module.exports = { getBook };
obj.test.js:
const obj = require('./obj');
const bookService = require('./bookService');
const sinon = require('sinon');
const test = require('ava');
test('getBook Error Block', (t) => {
const res = { error: new Error('network') };
const stub = sinon.stub(bookService, 'InfoRequest').callsFake((bookName, bookId, method, callback) => {
callback(res);
});
return obj.getBook().catch((res) => {
t.deepEqual(res.error, res.error);
sinon.assert.calledWith(stub, 'book', '111', 'GET', sinon.match.func);
});
});
test result:
> nyc ava --timeout=3000 "/Users/dulin/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/66702460/obj.test.js"
1 test passed
----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------|---------|----------|---------|---------|-------------------
All files | 71.43 | 16.67 | 75 | 71.43 |
bookService.js | 100 | 100 | 0 | 100 |
obj.js | 69.23 | 16.67 | 100 | 69.23 | 11-18
----------------|---------|----------|---------|---------|-------------------
I am trying to stub a module.exports function. But I have some trouble. I will give you a sudo code of the situation.
MyController.js
const sendOTPOnPhone = rewire('../../src/services/OtpService/sendOTPOnPhone')
module.exports = async function(req, res) {
const { error, data } = await sendOTPOnPhone(req.query.phone) //this is I want to stub
if(error)
return return res.send(error)
return res.send(data)
}
sendOTPService.js
module.exports = async function(phone) {
const result = await fetch(`external-api-call`)
if(result.status !== 'success')
return {
error: "Failed to send OTP!",
data: null
}
return {
error: null,
data: result
}
}
sendOTPTest.js
const expect = require('chai').expect
const request = require('supertest')
const sinon = require('sinon')
const rewire = require('rewire')
const sendOTPOnPhone = rewire('../../src/services/OtpService/sendOTPOnPhone')
const app = require('../../src/app')
describe('GET /api/v1/auth/otp/generate', function () {
it('should generate OTP', async () => {
let stub = sinon.stub().returns({
error: null,
data: "OTP sent"
})
sendOTPOnPhone.__set__('sendOTPOnPhone', stub)
const result = await request(app)
.get('/api/v1/auth/otp/generate?phone=8576863491')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
expect(stub.calledOnce).to.be.true
console.log(result.body)
// expect(result).to.equal('promise resolved');
})
})
Above test is failing, stub is not being called. I don't know what am I missing? If I do this in my sendOTPService:
const sendOTP = async function() {}
module.exports = {
sendOTP
}
and this in the controller.
const { error, data } = sendOTPOnPhone.sendOTPOnPhone(req.query.phone)
It works.
But I import it like const {sendOTPOnPhone } = require('../sendOTPService') It doesn't work.
I am aware that destructing changes the reference of the object.
Can someone suggest a workaround?
Is it possible to achieve this using rewire? OR It can be done with proxyquire.Please can someone suggest?
Here is the integration testing solution using proxyquire, you should use Globally override require.
app.js:
const express = require('express');
const controller = require('./controller');
const app = express();
app.get('/api/v1/auth/otp/generate', controller);
module.exports = app;
controller.js:
let sendOTPOnPhone = require('./sendOTPOnPhone');
module.exports = async function(req, res) {
const { error, data } = await sendOTPOnPhone(req.query.phone);
if (error) return res.send(error);
return res.send(data);
};
sendOTPOnPhone.js:
module.exports = async function(phone) {
const result = await fetch(`external-api-call`);
if (result.status !== 'success')
return {
error: 'Failed to send OTP!',
data: null,
};
return {
error: null,
data: result,
};
};
sendOTP.test.js:
const request = require('supertest');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('GET /api/v1/auth/otp/generate', function() {
it('should generate OTP', async () => {
let stub = sinon.stub().resolves({
error: null,
data: { message: 'OTP sent' },
});
stub['#global'] = true;
const app = proxyquire('./app', {
'./sendOTPOnPhone': stub,
});
const result = await request(app)
.get('/api/v1/auth/otp/generate?phone=8576863491')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
sinon.assert.calledOnce(stub);
console.log(result.body);
});
});
Integration test results with coverage report:
GET /api/v1/auth/otp/generate
{ message: 'OTP sent' }
✓ should generate OTP (2373ms)
1 passing (2s)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 68.75 | 25 | 50 | 73.33 |
app.js | 100 | 100 | 100 | 100 |
controller.js | 83.33 | 50 | 100 | 100 | 5
sendOTPOnPhone.js | 20 | 0 | 0 | 20 | 2-4,8
-------------------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60599945
I have answered a similar question here stub SMS otp method (without using external dependecy like proxyquire)
basically, the problem is with exports. Move your sendOtp method to a generalized place e.g User Model or schema. Import model, stub its function. It should work fine.
you are stubbing imported property called sendOtp instead of original function.
function.js
function sesSendEmail(message) {
var ses = new aws.SES({ apiVersion: '2020-12-01' });
var params = {
Source: 'xyz#gmail.com',
Template: 'deviceUsageStatisticsEmailTemplate',
Destination: {
ToAddresses: ['abc#gmail.com'],
},
TemplateData: message,
};
ses.sendTemplatedEmail(params, (err, data) => {
if (err) console.error;
// an error occurred
else console.log(data); // successful response
});
}
const exportFunctions = {
sesSendEmail: sesSendEmail,
};
module.exports = exportFunctions;
I want to write a test to verify params(Source, template..etc) so I was trying to mock sendTemplatedEmail.
Function.test.js
describe('sesSendEmail', () => {
const mocksendTemplatedEmail = {
sendTemplatedEmail: jest.fn()
}
index.sesSendEmail(mocksendTemplatedEmail);
test('Check if Source is correct', () => {
console.log(mocksendTemplatedEmail.sendTemplatedEmail.mock)
})
})
However, console.log is empty. Please advise.
Here is the unit test solution:
index.js:
const aws = require('aws-sdk');
function sesSendEmail(message) {
const ses = new aws.SES({ apiVersion: '2020-12-01' });
const params = {
Source: 'xyz#gmail.com',
Template: 'deviceUsageStatisticsEmailTemplate',
Destination: {
ToAddresses: ['abc#gmail.com'],
},
TemplateData: message,
};
ses.sendTemplatedEmail(params, (err, data) => {
if (err) {
return console.error(err);
}
console.log(data);
});
}
const exportFunctions = { sesSendEmail };
module.exports = exportFunctions;
index.test.js:
const index = require('.');
const aws = require('aws-sdk');
jest.mock('aws-sdk', () => {
const mSes = {
sendTemplatedEmail: jest.fn(),
};
return { SES: jest.fn(() => mSes) };
});
describe('59877312', () => {
let ses;
beforeEach(() => {
ses = new aws.SES();
});
afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
describe('sesSendEmail', () => {
it('should send templated email success', () => {
jest.spyOn(console, 'log');
const mData = {};
ses.sendTemplatedEmail.mockImplementationOnce((params, callback) => {
callback(null, mData);
});
const message = 'mock message';
index.sesSendEmail(message);
expect(aws.SES).toBeCalledWith({ apiVersion: '2020-12-01' });
expect(ses.sendTemplatedEmail).toBeCalledWith(
{
Source: 'xyz#gmail.com',
Template: 'deviceUsageStatisticsEmailTemplate',
Destination: {
ToAddresses: ['abc#gmail.com'],
},
TemplateData: message,
},
expect.any(Function),
);
expect(console.log).toBeCalledWith(mData);
});
it('should handle error', () => {
jest.spyOn(console, 'error');
const mError = new Error('network error');
ses.sendTemplatedEmail.mockImplementationOnce((params, callback) => {
callback(mError, null);
});
const message = 'mock message';
index.sesSendEmail(message);
expect(aws.SES).toBeCalledWith({ apiVersion: '2020-12-01' });
expect(ses.sendTemplatedEmail).toBeCalledWith(
{
Source: 'xyz#gmail.com',
Template: 'deviceUsageStatisticsEmailTemplate',
Destination: {
ToAddresses: ['abc#gmail.com'],
},
TemplateData: message,
},
expect.any(Function),
);
expect(console.error).toBeCalledWith(mError);
});
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/59877312/index.test.js (11.103s)
59877312
sesSendEmail
✓ should send templated email success (16ms)
✓ should handle error (3ms)
console.log node_modules/jest-mock/build/index.js:860
{}
console.error node_modules/jest-mock/build/index.js:860
Error: network error
at Object.it (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59877312/index.test.js:46:22)
at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process._tickCallback (internal/process/next_tick.js:68:7)
----------|----------|----------|----------|----------|-------------------|
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: 2 passed, 2 total
Snapshots: 0 total
Time: 12.485s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59877312
My code looks like this
const { MyClient } = require('some-service')
const invokeMe = async (input1, input2) => {
const client = new MyClient({
name: 'my-name'
})
return await client.invoke({
input1,
input2
}).catch((err) => {
throw err
})
}
This is what I have, how do I properly mock this service and spy on what it's called with?
const { MyClient } = require('some-service')
describe('test my client', () => {
it('invoke my client', () => {
const response = {
data: []
}
expect(invokeMe('abc', '123')).resolves.toEqual(response)
expect(MyClient).toBeCalledWith({
input1: 'abc',
input2: '123'
})
})
})
Edit: Why does the below still call the original function?
it('invoke my client', () => {
const mockInvoke = jest.fn().mockImplementation(() => Promise.resolve({
data: []
}))
const mockMyClient = () => {
return { invoke: mockInvoke }
}
const mockSomeService = {
MyClient: mockMyClient
}
jest.doMock('some-service', () => mockSomeService
...
})
You can use jest.mock(moduleName, factory, options) mock the imported service
E.g.
index.js:
const { MyClient } = require('./some-service');
const invokeMe = async (input1, input2) => {
const client = new MyClient({
name: 'my-name',
});
return await client
.invoke({
input1,
input2,
})
.catch((err) => {
throw err;
});
};
module.exports = invokeMe;
some-service.js:
class MyClient {
async invoke(input1, input2) {
return 'real response';
}
}
module.exports = { MyClient };
index.test.js:
const invokeMe = require('./');
const { MyClient } = require('./some-service');
jest.mock('./some-service', () => {
const mMyClient = { invoke: jest.fn() };
return { MyClient: jest.fn(() => mMyClient) };
});
describe('60008679', () => {
it('should invoke', async () => {
const client = new MyClient();
client.invoke.mockResolvedValueOnce('fake response');
const actual = await invokeMe('a', 'b');
expect(actual).toBe('fake response');
expect(MyClient).toBeCalledWith({ name: 'my-name' });
expect(client.invoke).toBeCalledWith({ input1: 'a', input2: 'b' });
});
it('should handle error', async () => {
const client = new MyClient();
const mError = new Error('some error');
client.invoke.mockRejectedValueOnce(mError);
await expect(invokeMe('a', 'b')).rejects.toThrowError(mError);
expect(MyClient).toBeCalledWith({ name: 'my-name' });
expect(client.invoke).toBeCalledWith({ input1: 'a', input2: 'b' });
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/60008679/index.test.js (11.029s)
60008679
✓ should invoke (8ms)
✓ should handle error (4ms)
----------|----------|----------|----------|----------|-------------------|
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: 2 passed, 2 total
Snapshots: 0 total
Time: 12.314s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/60008679