forJ
forJ

Reputation: 4627

How to mock external dependency in typescript using jest

I am hopelessly trying to mock firebase admin dependency in typescript for unit testing.

My actual function is as below

import {auth} from 'firebase-admin';

async validateUser(payload: string): Promise<User> {
  try {
    const resp = await auth().verifyIdToken(payload, true);

    return await this.userService.findByUsername(resp.email);
  } catch (e) {
    return null;
 }
}

I am trying to test the admin.auth().verifyIdToken(payload, true) by mocking the admin function. I attempted the test code like below

import {auth} from 'firebase-admin';

jest.mock('firebase-admin');

const mockedAuth = auth as jest.Mock;

mockedAuth.mockReturnValue({
  verifyIdToken: jest.fn().mockResolvedValue(true),
});

but I get an error saying TypeError: Cannot read property 'mockReturnValue' of undefined

if I am understanding correctly, I am mocking the entire firebase-admin package by calling jest.mock('firebase-admin'). So I would expect to be able to call mockReturnValue and again mock the verifyIdToken function within the auth function. However, I am not even able to call mockReturnValue because weirdly it doesn't seem to exist.

How do I need to write a code so that I can mock the resolved value from auth().verifyIdToken() ?

Upvotes: 2

Views: 7539

Answers (2)

hoangdv
hoangdv

Reputation: 16157

Use jest mock factory to mock firebase module.

jest.mock('firebase-admin', () => {
  return {
    auth: jest.fn(),
  }
});

Example for spec file:

import { auth } from 'firebase-admin';
import Test from './index';

jest.mock('firebase-admin', () => {
  return {
    auth: jest.fn(),
  }
});

describe("Test", () => {
  const email = '[email protected]';
  const user = { email, username: 'mocked-username' };

  let mockedAuth: jest.Mock;
  let mockVerifyIdToken: jest.Mock;
  let mockUserService: { findByUsername: jest.Mock }; // mock userService
  let testInstance: Test;

  beforeEach(() => {
    mockedAuth = auth as jest.Mock;
    mockVerifyIdToken = jest.fn()
    mockedAuth.mockReturnValue({
      verifyIdToken: mockVerifyIdToken,
    });

    mockUserService = {
      findByUsername: jest.fn(),
    };

    testInstance = new Test(mockUserService);
  });

  test("should return user info when pass correct payload", async () => {
    const payload = 'mocked-payload';

    mockVerifyIdToken.mockResolvedValue({ email });
    mockUserService.findByUsername.mockResolvedValue(user);

    const result = await testInstance.validateUser(payload);

    expect(result).toBe(user);
    expect(mockVerifyIdToken).toHaveBeenCalledWith(payload, true);
    expect(mockUserService.findByUsername).toHaveBeenCalledWith(email);
  });
})

Upvotes: 3

kdau
kdau

Reputation: 2099

The trouble in this case is that auth and similar exported functions in firebase-admin are defined as getters with Object.defineProperty. Unfortunately jest.mock can't see those when it evaluates the module, so it doesn't add mocks for them.

You'll need to mock auth (and any sibling functions you need) in a factory provided to jest.mock. Here's how, replacing the jest.mock line in your second code snippet:

jest.mock('firebase-admin', () =>
{
  const module = jest.createMockFromModule<any>('firebase-admin').default;
  module.auth = jest.fn();
  return module;
});

You could also choose to mock auth's return value within that factory to further streamline your code.

Upvotes: 2

Related Questions