Jake
Jake

Reputation: 3

How do I test if statement inside my mongoose pre-save hook

I'm new to testing and going for good code coverage.

I have a pre-save hook on my user model that deals with hashing a password, now I only want this to run when the password field has been modified.

How do I test this if statement? Is it a case of saving a user, updating a user, then saving it again and testing for the password?

Another question. What is the best practice here? To have it as a pre-save hook or a method on the user model?

UserSchema.pre('save', async function(next) {
  // Only run this function if password was modified
  if (!this.isModified('password')) return next();

  // Hash the password
  this.password = await bcrypt.hash(this.password, 12);

  // Remove passwordConfirm field
  this.passwordConfirm = undefined;

  next();
});

Upvotes: 0

Views: 902

Answers (1)

Lin Du
Lin Du

Reputation: 102207

Here is a unit test solution:

index.js:

import { Schema } from 'mongoose';
import bcrypt from 'bcrypt';

const UserSchema = new Schema();

UserSchema.pre('save', userPreSaveHook);

export async function userPreSaveHook(next) {
  // Only run this function if password was modified
  if (!this.isModified('password')) return next();

  // Hash the password
  this.password = await bcrypt.hash(this.password, 12);

  // Remove passwordConfirm field
  this.passwordConfirm = undefined;

  next();
}

index.spec.js:

import { userPreSaveHook } from './';

describe('userPreSaveHook', () => {
  test('should execute next middleware when password is modified', async () => {
    const mNext = jest.fn();
    const mContext = {
      isModified: jest.fn()
    };
    mContext.isModified.mockReturnValueOnce(false);
    await userPreSaveHook.call(mContext, mNext);
    expect(mContext.isModified).toBeCalledWith('password');
    expect(mNext).toBeCalledTimes(1);
  });

  test('should has password and remove passwordConfirm field', async () => {
    const mNext = jest.fn();
    const mContext = {
      isModified: jest.fn(),
      passwordConfirm: 'aaa',
      password: '123456'
    };
    mContext.isModified.mockReturnValueOnce(true);
    await userPreSaveHook.call(mContext, mNext);
    expect(mContext.isModified).toBeCalledWith('password');
    expect(mNext).toBeCalledTimes(1);
    expect(mContext.passwordConfirm).toBeUndefined();
    expect(mContext.password).not.toBe('123456');
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/58701700/index.spec.js (12.23s)
  userPreSaveHook
    ✓ should execute next middleware when password is modified (9ms)
    ✓ should has password and remove passwordConfirm field (295ms)

----------|----------|----------|----------|----------|-------------------|
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.689s, estimated 14s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58701700

Upvotes: 3

Related Questions