munkee
munkee

Reputation: 769

Jest: How to test the error routes in my code

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.

My coverage thus far: Code Coverage

Upvotes: 1

Views: 1159

Answers (1)

Lin Du
Lin Du

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

enter image description here

source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47587358

Upvotes: 1

Related Questions