Trying to run unit testing for a findAll() function. But getting this error " expect(received).toEqual(expected) // deep equality " - jestjs

Error message is this.
` PASS src/app.controller.spec.ts
FAIL src/users/services/users.service.spec.ts
● UsersService › should return all users
expect(received).toEqual(expected) // deep equality
- Expected - 6
+ Received + 1
- Object {
- "birthDate": 2010-06-07T00:00:00.000Z,
- "email": "xs#gsuj.lk",
- "id": 2,
- "name": "aq1s",
- }
+ Object {}
66 | //const dto ={id:2, name:'aq1s',birthDate: new Date('2010-06-07'), email : "xs#gsuj.lk" };
67 |
> 68 | expect( await service.findAll()).toEqual({
| ^
69 | id:2,
70 | name:'aq1s',
71 | birthDate: new Date('2010-06-07'),
at src/users/services/users.service.spec.ts:68:38
at fulfilled (src/users/services/users.service.spec.ts:5:58)
FAIL src/users/controllers/users.controller.spec.ts (5.09 s)
● UsersController › should findall
expect(received).toEqual(expected) // deep equality
- Expected - 6
+ Received + 1
- Object {
- "birthDate": 2010-06-07T00:00:00.000Z,
- "email": "xs#gsuj.lk",
- "id": 2,
- "name": "aq1s",
- }
+ Object {}
129 | const data = await controller.findAll();
130 |
> 131 | expect(data).toEqual({
| ^
132 | id: dto.id,
133 | name: dto.name,
134 | birthDate: dto.birthDate,
at src/users/controllers/users.controller.spec.ts:131:18
at fulfilled (src/users/controllers/users.controller.spec.ts:5:58)
Test Suites: 3 failed, 1 passed, 4 total
Tests: 2 failed, 9 passed, 11 total
Snapshots: 0 total
Time: 5.85 s
Ran all test suites.`
Users.controller.spec.ts file is this
describe('UsersController', () => {
let controller: UsersController;
const mockUsersService = {
create: jest.fn((dto) => {
return {
...dto,
};
}),
update: jest.fn((id, dto) => ({
id,
...dto,
})),
delete: jest.fn((id) => ({
id,
})),
findAll: jest.fn((dto) => {
return {
...dto,
};
}),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService],
})
.overrideProvider(UsersService)
.useValue(mockUsersService)
.compile();
//get service module from user module
controller = module.get<UsersController>(UsersController);
});
//check if service available
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should create', async () => {
const dto = {
id: 2,
name: 'aqs',
birthDate: new Date('2000-06-07'),
email: 'xs#gsuj.lk',
};
const data = await controller.create(dto);
expect(data).toEqual({
id: dto.id,
name: dto.name,
birthDate: dto.birthDate,
email: dto.email,
});
expect(controller).toBeDefined();
});
it('should findall', async () => {
const dto = {
id: 2,
name: 'aq1s',
birthDate: new Date('2010-06-07'),
email: 'xs#gsuj.lk',
};
const data = await controller.findAll();
expect(data).toEqual({
id: dto.id,
name: dto.name,
birthDate: dto.birthDate,
email: dto.email,
}),
expect(controller).toBeDefined();
});
});
user.service.spec.ts file is this.
describe('UsersService', () => {
let service: UsersService;
// let providers:UsersController;
const mockUsersRepository ={
create:jest.fn().mockImplementation(dto =>dto),
save:jest.fn().mockImplementation(user => Promise.resolve({id:2, ...user})),
update:jest.fn().mockImplementation((id,user) => Promise.resolve({id:2, ...user})),
delete:jest.fn().mockImplementation(id => Promise.resolve({id:2})),
find:jest.fn().mockImplementation(() => ({})),
}
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService, {
provide:getRepositoryToken(User),
useValue:mockUsersRepository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be create a new user record and return that', async () => {
expect( await service.create({id:2, name:'aqs',birthDate: new Date('2000-06-07'), email : "xs#gsuj.lk" })).toEqual({
id:2,
name:'aqs',
birthDate: new Date('2000-06-07'),
email : "xs#gsuj.lk" ,
});
});
it('should return all users', async () => {
//const dto ={id:2, name:'aq1s',birthDate: new Date('2010-06-07'), email : "xs#gsuj.lk" };
expect( await service.findAll()).toEqual({
id:2,
name:'aq1s',
birthDate: new Date('2010-06-07'),
email : "xs#gsuj.lk" ,
});
});
});
users.controller file is this
#Get()
async findAll() {
const users = await this.userService.findAll();
if (!users) {
return 'error while finding users';
}
return users;
}
users.service file is this
findAll(): Promise<User[]> {
return this.userRepository.find()
}

Fixed the issue by, user.controller.spec.ts
it('should find all', async () => {
const data = await controller.findAll();
expect(data).toEqual({}),
expect(controller).toBeDefined();
});
And user.service.spec.ts:
it('should return all users', async () => {
expect( await service.findAll()).toEqual({});
});

Related

How to use jest to keep track of how many times a nested function was called

Inside my test I want to be able to expect that analyzeArticle was called a set number of times. I need to mock the analyzation controller because it does a lot of async things that aren't important to this unit test.
const analyzeArticle = require('./analyzationController.js')
module.exports.consumer = async (event) => {
try {
const analyzationPromises = []
for (const record of event.Records) {
console.log('attempting to analyze: ', record.body)
analyzationPromises.push(analyzeArticle(record.body))
}
await Promise.allSettled(analyzationPromises)
} catch (err) {
console.log('reached top level with error: ', err)
}
}
My test looks like this
const consumer = require('../../analyzer/handler').consumer
const mockAnalyzeArticle = require('../../analyzer/analyzationController')
jest.mock('../../analyzer/analyzationController')
it('calls analyzer on all records', async () => {
await consumer({ Records: [{ body: 'fake body1' }, { body: 'fake body2' }] })
expect(mockAnalyzeArticle).toHaveBeenCalledTimes(2)
})
I get this error
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function analyzeArticle]
6 | it('calls analyzer on all records', async () => {
7 | await consumer({ Records: [{ body: 'fake body1' }, { body: 'fake body2' }] })
> 8 | expect(mockAnalyzeArticle).toHaveBeenCalledTimes(2)
| ^
9 | })
10 |

How do you mock Auth0 MangementClient using jest?

This is also a question on Auth0 node-auth0 library. The use case is that I am using the Auth0 create Actions API through Terraform and want to be able to writes tests against the Actions.
In this example I want to be able to test the onExecutePostLogin without using real values.
// auth0-post-login.js
exports.onExecutePostLogin = async (event, api) => {
const userId = event.user.user_id
const ManagementClient = require('auth0').ManagementClient
const management = new ManagementClient({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
audience: `https://${event.secrets.domain}/api/v2/`,
scope: 'read:users',
})
const params = { id: userId, page: 0, per_page: 50, include_totals: false }
let userPermissions = await management.getUserPermissions(params)
const map = require('array-map')
const permissions = map(userPermissions, function(permission) {
return permission.permission_name
})
api.accessToken.setCustomClaim('https://example.com/access', permissions.join(' '))
}
One of the main issues is that the functions like getUserPermissions is created through their utility wrapper:
utils.wrapPropertyMethod(ManagementClient, 'getUserPermissions', 'users.getPermissions');
This causes jest to have issues finding the functions.
I did something similar as stsmurf to mock the response of the Auth0 methods.
I have a file where I store my helper methods like "find a role by its name"
// helper.ts
import { ManagementClient } from 'auth0';
export const getRoleByName = async (roleName: string) => {
const api = new ManagementClient({
clientId: clientId,
clientSecret: clientSecret,
domain: domain,
});
const roles = await api.getRoles();
const role = roles.find((r) => r.name == roleName);
if (!role) throw new Error('Role not found');
return role;
};
// helper.test.ts
import { Role } from 'auth0';
import { getRoleByName } from './helpers';
const mockGetRoles = jest.fn();
jest.mock('auth0', () => {
return {
ManagementClient: jest.fn().mockImplementation(() => {
return {
getRoles: mockGetRoles,
};
}),
};
});
describe('Get role', () => {
beforeAll(() => {
const dummyRoles: Role[] = [
{ id: 'fake_id_1', description: 'Fake role nr 1', name: 'Fake Role 1' },
{ id: 'fake_id_2', description: 'Fake role nr 2', name: 'Fake Role 2' },
];
mockGetRoles.mockImplementation(() => dummyRoles);
});
it('can return a role if it exists', async () => {
const expectedResult: Role = {
id: 'fake_id_1',
description: 'Fake role nr 1',
name: 'Fake Role 1',
};
const result = await getRoleByName('Fake Role 1');
expect(expectedResult).toEqual(result);
});
it('will throw an error when a role is not found', async () => {
await expect(getRoleByName('Fake Role 3')).rejects.toThrowError();
});
});
In order to get to functions like getUserPermissions I needed to override the implementation of the ManagementClient. This allowed me to then define getUserPermissions as a mock function.
import { onExecutePostLogin } from './auth0-post-login'
const mockManagementClient = jest.fn()
const mockGetUserPermissions = jest.fn()
jest.mock('auth0', () => {
return {
ManagementClient: (opts) => {
mockManagementClient(opts)
return {
getUserPermissions: (params) => {
return mockGetUserPermissions(params)
},
}
},
}
})
describe('onExecutePostLogin', () => {
const mockSetCustomClaim = jest.fn()
beforeEach(() => {
mockSetCustomClaim.mockClear()
mockManagementClient.mockClear()
mockGetUserPermissions.mockClear()
mockGetUserPermissions.mockReturnValue([
{
permission_name: 'read:foo',
},
{
permission_name: 'update:bar',
},
])
})
const event = {
user: {
user_id: 'abcd123',
},
secrets: {
domain: 'test-example.us.auth0.com',
clientId: 'a-client-id',
clientSecret: 'a-client-secret',
},
}
const api = {
accessToken: {
setCustomClaim: mockSetCustomClaim,
},
}
it('initializes a ManagementClient', async () => {
await onExecutePostLogin(event, api)
expect(mockManagementClient).toHaveBeenCalledWith({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
audience: `https://${event.secrets.domain}/api/v2/`,
scope: 'read:users',
})
})
it('gets users permissions', async () => {
await onExecutePostLogin(event, api)
expect(mockGetUserPermissions).toHaveBeenCalledWith(
{ id: event.user.user_id, page: 0, per_page: 50, include_totals: false },
)
})
it('sets custom claims', async () => {
await onExecutePostLogin(event, api)
const expectedPermissions = 'read:foo update:bar'
expect(mockSetCustomClaim).toHaveBeenCalledWith(
'https://example.com/access', expectedPermissions,
)
})
})

How to mock AWS TimestreamWrite by jest

This project is to record data by AWS Timestream, and it works well.
However, I'm failed to mock AWS TimestreamWrite by using jest. I tried some ways but not working. Can someone help me?
My files as below:
ledger-service.js
const AWS = require("aws-sdk");
const enums = require("./enums");
var https = require("https");
var agent = new https.Agent({
maxSockets: 5000,
});
const tsClient = new AWS.TimestreamWrite({
maxRetries: 10,
httpOptions: {
timeout: 20000,
agent: agent,
},
});
module.exports = {
log: async function (audit) {
try {
if (Object.keys(audit).length !== 0) {
if (!isPresent(audit, "name")) {
throw new Error("Name shouldn't be empty");
}
if (!isPresent(audit, "value")) {
throw new Error("Value shouldn't be empty");
}
return await writeRecords(recordParams(audit));
} else {
throw new Error("Audit object is empty");
}
} catch (e) {
throw new Error(e);
}
},
};
function isPresent(obj, key) {
return obj[key] != undefined && obj[key] != null && obj[key] != "";
}
function recordParams(audit) {
const currentTime = Date.now().toString(); // Unix time in milliseconds
const dimensions = [
// { Name: "client", Value: audit["clientId"] },
{ Name: "user", Value: audit["userId"] },
{ Name: "entity", Value: audit["entity"] },
{ Name: "action", Value: audit["action"] },
{ Name: "info", Value: audit["info"] },
];
return {
Dimensions: dimensions,
MeasureName: audit["name"],
MeasureValue: audit["value"],
MeasureValueType: "VARCHAR",
Time: currentTime.toString(),
};
}
function writeRecords(records) {
try {
const params = {
DatabaseName: enums.AUDIT_DB,
TableName: enums.AUDIT_TABLE,
Records: [records],
};
return tsClient.writeRecords(params).promise();
} catch (e) {
throw new Error(e);
}
}
ledger-service.spec.js
const AWS = require("aws-sdk");
const audit = require("./ledger-service");
describe("ledger-service", () => {
beforeEach(async () => {
jest.resetModules();
});
afterEach(async () => {
jest.resetAllMocks();
});
it("It should write records when all success", async () => {
const mockAudit={
name: 'testName',
value: 'testValue',
userId: 'testUserId',
entity: 'testEntity',
action: 'testAction',
info: 'testInfo',
};
const mockWriteRecords = jest.fn(() =>{
console.log('mock success')
return { promise: ()=> Promise.resolve()}
});
const mockTsClient={
writeRecords: mockWriteRecords
}
jest.spyOn(AWS,'TimestreamWrite');
AWS.TimestreamWrite.mockImplementation(()=>mockTsClient);
//a=new AWS.TimestreamWrite();
//a.writeRecords(); //these two lines will pass the test and print "mock success"
await audit.log(mockAudit); //this line will show "ConfigError: Missing region in config"
expect(mockWriteRecords).toHaveBeenCalled();
});
});
I just think the the AWS I mocked doesn't pass into the ledger-service.js. Is there a way to fix that?
Thanks
updates: Taking hoangdv's suggestion
I am thinking jest.resetModules(); jest.resetAllMocks(); don't work. If I put the "It should write records when all success" as the first test, it will pass the test. However, it will fail if there is one before it.
Pass
it("It should write records when all success", async () => {
const mockAudit = {
name: 'testName',
value: 'testValue',
userId: 'testUserId',
entity: 'testEntity',
action: 'testAction',
info: 'testInfo',
};
await audit.log(mockAudit);
expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
maxRetries: 10,
httpOptions: {
timeout: 20000,
agent: expect.any(Object),
},
});
expect(mockWriteRecords).toHaveBeenCalled();
});
it("It should throw error when audit is empty", async () => {
const mockAudit = {};
await expect(audit.log(mockAudit)).rejects.toThrow(`Audit object is empty`);
});
Failed
it("It should throw error when audit is empty", async () => {
const mockAudit = {};
await expect(audit.log(mockAudit)).rejects.toThrow(`Audit object is empty`);
});
it("It should write records when all success", async () => {
const mockAudit = {
name: 'testName',
value: 'testValue',
userId: 'testUserId',
entity: 'testEntity',
action: 'testAction',
info: 'testInfo',
};
await audit.log(mockAudit);
expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
maxRetries: 10,
httpOptions: {
timeout: 20000,
agent: expect.any(Object),
},
});
expect(mockWriteRecords).toHaveBeenCalled();
});
In ledger-service.js you call new AWS.TimestreamWrite "before" module.exports, this means it will be called with actual logic instead of mock.
The solution is just mock AWS before you call require("./ledger-service");
ledger-service.spec.js
const AWS = require("aws-sdk");
describe("ledger-service", () => {
let audit;
let mockWriteRecords;
beforeEach(() => {
mockWriteRecords = jest.fn(() => {
return { promise: () => Promise.resolve() }
});
jest.spyOn(AWS, 'TimestreamWrite');
AWS.TimestreamWrite.mockImplementation(() => ({
writeRecords: mockWriteRecords
}));
audit = require("./ledger-service"); // this line
});
afterEach(() => {
jest.resetModules(); // reset module to update change for each require call
jest.resetAllMocks();
});
it("It should write records when all success", async () => {
const mockAudit = {
name: 'testName',
value: 'testValue',
userId: 'testUserId',
entity: 'testEntity',
action: 'testAction',
info: 'testInfo',
};
await audit.log(mockAudit);
expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
maxRetries: 10,
httpOptions: {
timeout: 20000,
agent: expect.any(Object),
},
});
expect(mockWriteRecords).toHaveBeenCalled();
});
});

Mocking/stubbing a class with Jest in Node.JS

I've got a class that sends a Slack message that looks like this:
class SlackMessage {
constructor(email_address, message) {
this.email_address = email_address;
this.message = message;
this.slackClient = new WebClient(token);
}
async send() {
let user = await this.user();
return await this.slackClient.chat.postMessage({
channel: user.user.id,
text: message,
});
}
async user() {
return await this.slackClient.users.lookupByEmail({
email: email_address
});
}
}
I want to mock the slackClient, so that slackClient.users.lookupByEmail returns a known value, and I can check this.slackClient.chat.postMessage gets called with the expected args, but I can't figure out how to do this. I've tried this (and various other permuations):
jest.mock("#slack/web-api");
describe("SlackMessage", () => {
beforeAll(() => {
WebClient.mockImplementation(() => {
return {
chat: {
postMessage: jest.fn(() => {
return Promise.resolve({})
})
},
users: {
lookupByEmail: jest.fn(() => {
return Promise.resolve({
user: {
id: "1234"
}
})
})
}
};
});
});
test("it sends a slack message", async () => {
message = new SlackMessage("foo#example.com", "Hello there!");
message.send()
expect(message.slackClient.users.lookupByEmail).toHaveBeenCalledWith({
email: "foo#example.com"
})
expect(message.slackClient.chat.postMessage).toHaveBeenCalledWith({
channel: "1234",
text: "Hello there!",
})
})
})
But this gives me the error:
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: {"email": "foo#example.com"}
Number of calls: 0
33 | message.send()
34 |
> 35 | expect(message.slackClient.users.lookupByEmail).toHaveBeenCalledWith({
| ^
36 | email: "foo#example.com"
37 | })
38 | expect(message.slackClient.chat.postMessage).toHaveBeenCalledWith({
at Object.<anonymous> (spec/slack_message.test.js:35:53)
Where am I going wrong?

How does one properly to unit test Joi Schemas validation?

I created a Joi validation schema that gets called in my routes. However when I run a code coverage that file is NOT being covered. So, I am trying to write a test for it.
Validator.js
const Joi = require('joi');
module.exports = {
validateExternalId: (schema, name) => {
return (req, res, next) => {
const result = Joi.validate({ param: req.params[name] }, schema);
if (result.error) {
return res.status(400).send(result.error.details[0].message);
}
next();
};
},
schemas: {
idSchema: Joi.object().keys({
param: Joi.string().regex(/^[a-zA-Z0-9]{20}$/).required()
})
}
};
Validator.test.js
const { validateExternalId, schemas } = require('../../src/helpers/validation');
const app = require('../../src/router')
const mockResponse = () => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
};
describe('Testing validateExternalId schema', () => {
it('It can validate the external Id Regex length', done => {
const req = {
params: [
{
extClientId: 'abcdefghij0123456789'
}
]
};
app.use('/token/:extClientId', validateExternalId(schemas.idSchema, 'extClientId');
// expect().toHaveBeenCalled();
});
});
Please Go Easy on ME... Here is my attempt on testing this Joi validator. I tried to but my expected wasn't working so I commented it out for now. any pointers would be appreciated. thank you
Here is the unit test solution:
validator.js:
const Joi = require('joi');
module.exports = {
validateExternalId: (schema, name) => {
return (req, res, next) => {
const result = Joi.validate({ param: req.params[name] }, schema);
if (result.error) {
return res.status(400).send(result.error.details[0].message);
}
next();
};
},
schemas: {
idSchema: Joi.object().keys({
param: Joi.string()
.regex(/^[a-zA-Z0-9]{20}$/)
.required(),
}),
},
};
validator.test.js:
const { validateExternalId, schemas } = require('./validator');
const Joi = require('joi');
describe('60730701', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should send error', () => {
const validationResults = { error: { details: [{ message: 'validation error' }] } };
const validateSpy = jest.spyOn(Joi, 'validate').mockReturnValueOnce(validationResults);
const mReq = { params: { extClientId: '123' } };
const mRes = { status: jest.fn().mockReturnThis(), send: jest.fn() };
validateExternalId(schemas.idSchema, 'extClientId')(mReq, mRes);
expect(validateSpy).toBeCalledWith({ param: '123' }, schemas.idSchema);
expect(mRes.status).toBeCalledWith(400);
expect(mRes.send).toBeCalledWith('validation error');
});
it('should pass the validation and call api', () => {
const validationResults = { error: undefined };
const validateSpy = jest.spyOn(Joi, 'validate').mockReturnValueOnce(validationResults);
const mReq = { params: { extClientId: '123' } };
const mRes = {};
const mNext = jest.fn();
validateExternalId(schemas.idSchema, 'extClientId')(mReq, mRes, mNext);
expect(validateSpy).toBeCalledWith({ param: '123' }, schemas.idSchema);
expect(mNext).toBeCalled();
});
});
unit test results with 100% coverage:
PASS stackoverflow/60730701/validator.test.js (9.96s)
60730701
✓ should send error (6ms)
✓ should pass the validation and call api (2ms)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
validator.js | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 11.647s, estimated 15s
source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60730701

Resources