简体   繁体   中英

Jest spyOn.mockImplementation calls actual method

I have a middleware function:

// middlewares/auth.middleware.ts

export async function verifyUser(
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    // verify token logic

    req.user = user;  // I add the verified user details to req

    next();
  } catch (error) {
    console.log(error);
    res
      .status(401)
      .json({ error: 'You are not authorized to make this request' });
  }
}

In my test, I'm doing the following:

import * as middleware from '../../middlewares/auth.middleware';

const spy = jest.spyOn(middleware, 'verifyUser');

describe('POST /api/users', () => {
  test('when the user has ONLY been created in external auth', async () => {
    spy.mockImplementation(
      jest.fn(async (req, _res, next) => {
        req.user = {
          id: '1',
          email: 'user1@email.com',
          fullName: null,
        };
        next();
      })
    );

    const res = await request(app)
      .post('/api/users')
      .send({ fullName: 'Jimmy Neutron' });

    expect(res.status).toEqual(200);

    spy.mockRestore();
  });
});

However this calls the original verifyUser function, instead of the what I added.

But, if I add this to the test file then my test passes:

jest.mock('../../middlewares/auth.middleware', () => ({
  verifyUser: jest.fn((req, _res, next) => {
    req.user = {
      id: '1',
      email: 'user1@email.com',
      fullName: null
    };
    next();
  }),
}));

How can I make my code work so that I can change the implementation per test?

If you look at the jest.spyOn docs you can see this:

Note: By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or object[methodName] = jest.fn(() => customImplementation);

So this explains why adding a mock makes your tests pass.

Depending on what version of jest you are running, there is a very useful helper method that you can now use, jest.mocked :

jest.mocked<T>(item: T, deep = false)

In your case you could:

  1. Add a mockImplementation to your spyOn :
const spy = jest.spyOn(middleware, 'verifyUser').mockImplementation(jest.fn((req, _res, next) => {
    req.user = {
      id: '1',
      email: 'user1@email.com',
      fullName: null
    };
    next();
  });

  // ...
  1. Remove the spyOn entirely and refactor as follows:
import * as middleware from '../../middlewares/auth.middleware';

jest.mock('../../middlewares/auth.middleware');

const mockMiddleware = jest.mocked(middleware);
const mockVerifyUser = jest.fn(async (req, _res, next) => {
        req.user = {
          id: '1',
          email: 'user1@email.com',
          fullName: null,
        };
        next();
      });

describe('POST /api/users', () => {
  test('when the user has ONLY been created in external auth', async () => {
    mockMiddleware.mockImplementation(() => ({
         verifyUser: mockVerifyUser,
    });

// ...

But the two approaches are equivalent.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM