Lin Du
Lin Du

Reputation: 102577

How to mock a module with typescript namespace?

This is the function under test:

import * as firebase from 'firebase';

function signInWithGoogle() {
  var provider = new firebase.auth.GoogleAuthProvider();
  firebase.auth().signInWithRedirect(provider);
}

firebase has below type definition:

declare namespace firebase.auth 

and

function auth(app?: firebase.app.App): firebase.auth.Auth;
jest.mock('firebase', () => {
  // Don't know how to mock, what should be returned?  
})

I need mock firebase.auth.GoogleAuthProvider(), firebase.auth() and signInWithRedirect methods of firebase.

But the firebase.auth can be an object or a function. What should be returned in the factory of jest.mock?

Does jestjs support this kind of mock or spy? Thanks guys.

Upvotes: 3

Views: 4334

Answers (2)

Lin Du
Lin Du

Reputation: 102577

From @Teneff's answer, I figure it out. The point is like @Teneff said, typescript namespace is some kind of a JS function after compiling and type erasure. After knowing this, of course, you can assign properties to JS function. Here is the completed working code example:

index.ts:

import firebase from 'firebase';

export function signInWithgoogle() {
  const provider = new firebase.auth.GoogleAuthProvider();
  firebase.auth().signInWithRedirect(provider);
}

For simple, I add @ts-ignore comment in order to ignore the type check.

index.spec.ts:

import { signInWithgoogle } from './';
import firebase from 'firebase';

jest.mock('firebase', () => {
  const auth = jest.fn();
  const mAuth = { signInWithRedirect: jest.fn() };
  // @ts-ignore
  auth.GoogleAuthProvider = jest.fn();
  // @ts-ignore
  auth.Auth = jest.fn(() => mAuth);
  return { auth };
});

describe('signInWithgoogle', () => {
  it('should mock firebase with typescript namespace', () => {
    // @ts-ignore
    firebase.auth.mockImplementation(() => new firebase.auth.Auth());
    signInWithgoogle();
    expect(firebase.auth.GoogleAuthProvider).toBeCalledTimes(1);
    expect(firebase.auth).toBeCalledTimes(1);
    expect(firebase.auth().signInWithRedirect).toBeCalledTimes(1);
  });
});

Unit test result:

 PASS  src/mock-ts-namespace/index.spec.ts
  signInWithgoogle
    ✓ should mock firebase with typescript namespace (7ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.964s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/mock-ts-namespace

Upvotes: 2

Teneff
Teneff

Reputation: 32148

Javascript functions are objects of type Function. And to every object you can assign properties so you can mock it as:

jest.mock('firebase', () => {
  const auth = jest.fn();
  auth.GoogleAuthProvider = jest.fn();
  auth.Auth = jest.fn();
  return { auth };
});

or you can use Jest's auto-mocking without factory function:

jest.mock('firebase');

and then mock the implementation

// based on your question (not tested)
firebase.auth.mockImplementation(() => new firebase.auth.Auth())

Upvotes: 4

Related Questions