Reputation: 769
I have the following function within my ES6 User class which searches for a user given a string.
// Search for a user by their pNick, includes partial matching
static getBypNick(pNick = '') {
// Define our search criteria regex and normalise to lower case
const userSearchRegex = new RegExp(`^${pNick.toLowerCase()}`, 'i')
return new Promise((resolve, reject) => {
// Search user collection for pNick using regex like search and return array of results
userTable.find({
_pNick: userSearchRegex
}).sort({
_pNick: 1
}).exec(function (err, result) {
// If error reject
if (err) {
return reject(err)
}
const userArray = result.map((user) => {
return new User(
user._pNick,
user._firstName,
user._userName,
user._phoneNumber,
user._userID)
})
// Return user records if found
return resolve(userArray)
})
})
}
Whilst I can easily test the success routes using Jest I'm struggling to understand how I can invoke the error cases, especially around the .exec method within the function to invoke my reject routes in the promise.
I understand that I can use various Jest features such as mockImplementation but I just can't figure out the best case in this scenario. The database being used behind the scenes is NeDB, I'm pretty positive I just need to force the .exec portion to return an error and then I should be catching this in my promise.
I have no intention of testing the underlying NeDB library as it has its own tests which execute successfully so this is really all about my own methods.
Upvotes: 1
Views: 1159
Reputation: 102207
Here is the unit test solution:
userRepo.js
:
import { userTable } from './userTable';
import { User } from './user';
export class UserRepo {
static getBypNick(pNick = '') {
const userSearchRegex = new RegExp(`^${pNick.toLowerCase()}`, 'i');
return new Promise((resolve, reject) => {
userTable
.find({
_pNick: userSearchRegex,
})
.sort({
_pNick: 1,
})
.exec(function(err, result) {
if (err) {
return reject(err);
}
const userArray = result.map((user) => {
return new User(user._pNick, user._firstName, user._userName, user._phoneNumber, user._userID);
});
return resolve(userArray);
});
});
}
}
userTable.js
:
const userTable = {
find() {
return this;
},
sort() {
return this;
},
exec(fn) {
console.log('real exec');
fn();
},
};
user.js
:
export class User {
constructor(nick, firstName, userName, phoneNumber, userId) {
this.nick = nick;
this.firstName = firstName;
this.userName = userName;
this.phoneNumber = phoneNumber;
this.userId = userId;
}
}
userRepo.test.js
:
import { UserRepo } from './userRepo';
import { userTable } from './userTable';
jest.mock('./userTable', () => {
const mUserTable = {
find: jest.fn().mockReturnThis(),
sort: jest.fn().mockReturnThis(),
exec: jest.fn(),
};
return { userTable: mUserTable };
});
describe('47587358', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should get user by nick', async () => {
const mResult = [{ _pNick: '_pNick', _firstName: '_firstName', _userName: '_userName', _phoneNumber: 123456, _userID: 1 }];
userTable.exec.mockImplementationOnce((fn) => {
fn(null, mResult);
});
const actual = await UserRepo.getBypNick('jest');
expect(actual).toEqual(
expect.arrayContaining([
expect.objectContaining({
nick: expect.any(String),
firstName: expect.any(String),
userName: expect.any(String),
phoneNumber: expect.any(Number),
userId: expect.any(Number),
}),
]),
);
expect(userTable.find).toBeCalledWith({ _pNick: new RegExp(`^jest`, 'i') });
expect(userTable.sort).toBeCalledWith({ _pNick: 1 });
expect(userTable.exec).toBeCalledWith(expect.any(Function));
});
it('should handle error', async () => {
const mError = new Error('network');
userTable.exec.mockImplementationOnce((fn) => {
fn(mError, null);
});
await expect(UserRepo.getBypNick('jest')).rejects.toThrowError('network');
expect(userTable.find).toBeCalledWith({ _pNick: new RegExp(`^jest`, 'i') });
expect(userTable.sort).toBeCalledWith({ _pNick: 1 });
expect(userTable.exec).toBeCalledWith(expect.any(Function));
});
});
unit test result with coverage report:
PASS src/stackoverflow/47587358/userRepo.test.js (9.448s)
47587358
✓ should get user by nick (10ms)
✓ should handle error (3ms)
-------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files | 100 | 66.67 | 100 | 100 | |
user.js | 100 | 100 | 100 | 100 | |
userRepo.js | 100 | 66.67 | 100 | 100 | 5 |
-------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.66s
source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47587358
Upvotes: 1