有没有办法开玩笑地模拟 firebase 模块?

Is there a way to mock firebase modules in jest?

Code.js

saveToFirebase = () => {
    let statusMsg = ""

    firebase
      .firestore()
      .collection("messages")
      .doc(this.state.field.email)
      .set(this.state.field)
      .then((statusMsg = "Your Message have been submitted successfully."))

    this.clearFields(statusMsg)
  }

Code.test.js

it("should call mock firebase module", () => {
            const docData = {
              field: {
                name: "Rob Bob",
                email: "rob@bob.com",
                phone: "9999999999",
                subject: "Test subject",
                message: "Test message",
              },
            }

            const docResult = {
              data: () => docData,
            }

            const get = jest.fn(() => Promise.resolve(docResult))
            const set = jest.fn()

            const doc = jest.fn(() => {
              return {
                set,
                get,
              }
            })
            const colllection = jest.fn((messages) => {
              return { doc }
            })
            const firestore = () => {
              return colllection
            }

            firebase.firestore = firestore

            const mockData = { fake: "data" }
            jest.clearAllMocks()
            wrapper.instance().saveToFirebase()
          })

虽然运行 Code.test.js,但它抛出一个错误,即firebase.firestore().collection() 不是一个函数。请建议对上述代码的 firebase 模块进行适当的模拟,并添加一个案例以检查 firebase 是否已成功返回,然后调用 clearFields()。

您可以使用 jest.spyOn(object, methodName) 模拟 firebase.firestore() 函数。

例如 index.jsx:

import React, { Component } from 'react';
import firebase from 'firebase';

export default class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      field: { email: 'fuck@qq.com' },
    };
  }
  clearFields(statusMsg) {
    console.log(statusMsg);
  }
  saveToFirebase = () => {
    let statusMsg = '';

    return firebase
      .firestore()
      .collection('messages')
      .doc(this.state.field.email)
      .set(this.state.field)
      .then(() => {
        statusMsg = 'Your Message have been submitted successfully.';
        this.clearFields(statusMsg);
      });
  };
  render() {
    return <div>my component</div>;
  }
}

index.test.jsx:

import MyComponent from '.';
import { shallow } from 'enzyme';
import React from 'react';
import firebase from 'firebase';

describe('61358076', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should pass', async () => {
    const firestoreMock = {
      collection: jest.fn().mockReturnThis(),
      doc: jest.fn().mockReturnThis(),
      set: jest.fn().mockResolvedValueOnce(),
    };
    const clearFieldsSpy = jest.spyOn(MyComponent.prototype, 'clearFields');
    jest.spyOn(firebase, 'firestore').mockImplementationOnce(() => firestoreMock);
    const wrapper = shallow(<MyComponent></MyComponent>);
    await wrapper.instance().saveToFirebase();
    expect(firestoreMock.collection).toBeCalledWith('messages');
    expect(firestoreMock.doc).toBeCalledWith('fuck@qq.com');
    expect(firestoreMock.set).toBeCalledWith({ email: 'fuck@qq.com' });
    expect(clearFieldsSpy).toBeCalledWith('Your Message have been submitted successfully.');
  });
});

100% 覆盖率的单元测试结果:

 PASS  Whosebug/61358076/index.test.jsx (17.824s)
  61358076
    ✓ should pass (39ms)

  console.log Whosebug/61358076/index.jsx:1611
    Your Message have been submitted successfully.

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

源代码:https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/Whosebug/61358076

嗨,最近我需要模拟 jest 模块,这是我的实现, 注意:我使用 eventEmitter3 来处理 onAuthStateChange

import EventEmitter from 'eventemitter3';

const authEmitter = new EventEmitter();
let isSignIn = true;
const user = {
    displayName: 'test name',
    email: 'redirectTest@test.com',
    emailVerified: true,
    uid: 'id123',
    providerData: [
        {
            email: 'redirectTest@test.com',
            displayName: 'redirectResultTestDisplayName',
            providerId: 'google',
        },
    ],
};

const mockFirebase: any = {
    initializeApp: jest.fn().mockReturnValue({
        auth: jest.fn().mockReturnValue({
            currentUser: isSignIn
                ? {
                        displayName: 'redirectResultTestDisplayName',
                        email: 'redirectTest@test.com',
                        emailVerified: true,
                        uid: 'id123',
                        providerData: [
                            {
                                email: 'redirectTest@test.com',
                                displayName: 'redirectResultTestDisplayName',
                                providerId: 'google',
                            },
                        ],
                        sendEmailVerification: jest.fn(),
                  }
                : null,
            signInWithRedirect: jest.fn(),
            getRedirectResult: jest.fn().mockReturnValue({
                credential: {
                    providerId: 'Google',
                },
                user: {
                    getIdToken: jest.fn().mockResolvedValue('abc1234'),
                },
                additionalUserInfo: {
                    profile: {
                        email: '__tests__@__tests__.com',
                        name: 'John Doe',
                    },
                },
            }),
            onAuthStateChanged: jest.fn(fn => {
                // sign user on start
                fn(user);
                // sign-out user on start
                authEmitter.on('sign-out', fn, undefined);
                authEmitter.on('sign-in', fn, user);
            }),
            signOut: jest.fn(() => {
                isSignIn = false;
                authEmitter.emit('sign-out');
            }),
            signInWithEmailAndPassword: jest.fn(() => {
                isSignIn = true;
                authEmitter.emit('sign-in', user);
                return Promise.resolve(true);
            }),
            sendPasswordResetEmail: jest.fn(() => Promise.resolve(true)),
            sendEmailVerification: jest.fn(() => Promise.resolve(true)),
            signInWithPopup: jest.fn(() => {
                isSignIn = true;
                authEmitter.emit('sign-in', user);
                return Promise.resolve(true);
            }),
        }),
        firestore: jest.fn().mockReturnValue({
            collection: jest.fn().mockReturnValue({
                doc: jest.fn().mockReturnValue({
                    add: jest.fn().mockResolvedValue({
                        id: 'abc123',
                    }),
                    set: jest.fn().mockResolvedValue({
                        uid: 'abc123',
                    }),
                }),
            }),
        }),
    }),
    auth: {
        GoogleAuthProvider: class {
            addScope = jest.fn();
        },
        GithubAuthProvider: class {
            addScope = jest.fn();
        },
        FacebookAuthProvider: class {
            addScope = jest.fn();
        },
    },
};

jest.mock('firebase/app', () => mockFirebase);