reactjs - 如何模拟 Firebase Auth 方法? ( react ,测试库)

标签 reactjs firebase jestjs mocking react-testing-library

我有以下组件:

import React from "react";
import Firebase from "../../Firebase";

const SignOutButton = () => (
  <button type="button" onClick={() => Firebase.auth().signOut()}>
    Sign Out
  </button>
);

export default SignOutButton;

我想测试 Firebase.auth().signOut 是否调用了 onClick

我在其他地方找到了 Firebase.auth 的模拟:

const authMock = jest.fn(() => {
  return {
    createUserAndRetrieveDataWithEmailAndPassword: jest.fn(() =>
      Promise.resolve(true)
    ),
    sendPasswordResetEmail: jest.fn(() => Promise.resolve(true)),
    signInAndRetrieveDataWithEmailAndPassword: jest.fn(() =>
      Promise.resolve(true)
    ),
    fetchSignInMethodsForEmail: jest.fn(() => Promise.resolve(true)),
    signOut: jest.fn(() => {
      Promise.resolve(true);
    }),
    onAuthStateChanged: jest.fn(),
    currentUser: {
      sendEmailVerification: jest.fn(() => Promise.resolve(true))
    }
  };
});

export { authMock };

在 SignOutButton.test 中我有:

import React from "react";
import { render, cleanup, fireEvent } from "@testing-library/react";
import SignOutButton from "../.";
import Firebase from "../../../Firebase";
import { authMock } from "../../../../setupTests";
// @ts-ignore
Firebase.auth = authMock;

describe("<SignOutButton />", () => {
  afterEach(cleanup);

  it("calls Firebase signOut on click", async () => {
    const { getByText } = render(<SignOutButton />);
    const button = getByText("Sign Out");

    fireEvent.click(button);

    expect(Firebase.auth().signOut).toHaveBeenCalled();
  });
});

我的测试结果是预期调用为 1 但接收到 0。

我做错了什么?

谢谢!

最佳答案

您的测试存在以下问题:

  1. 在将 Firebase.auth 方法替换为 authMock 之后,您应该导入 SignOutButton 组件。否则,Firebase.auth 方法是原始版本而不是模拟版本。您可以使用 console.log(Firebase.auth) 进行检查。

  2. 您应该为 Firebase.auth 方法的返回值返回相同的引用。否则,断言将失败。

完整的单元测试解决方案:

SignOutButton.tsx:

import React from 'react';
import Firebase from './firebase';

console.log(Firebase.auth);
console.log('should keep same reference to authObject:', Firebase.auth() === Firebase.auth());

const SignOutButton = () => (
  <button type="button" onClick={() => Firebase.auth().signOut()}>
    Sign Out
  </button>
);

export default SignOutButton;

firebase.ts:

export default {
  auth() {
    console.log('auth real implementation');
    return this;
  },
  async signOut() {
    console.log('signOut real implementation');
  },
};

setupTests.ts:

const authObjectMock = {
  createUserAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
  sendPasswordResetEmail: jest.fn(() => Promise.resolve(true)),
  signInAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
  fetchSignInMethodsForEmail: jest.fn(() => Promise.resolve(true)),
  signOut: jest.fn(() => {
    Promise.resolve(true);
  }),
  onAuthStateChanged: jest.fn(),
  currentUser: {
    sendEmailVerification: jest.fn(() => Promise.resolve(true)),
  },
};
const authMock = jest.fn(() => authObjectMock);

export { authMock };

SignOutButton.test.tsx:

import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import Firebase from './firebase';
import { authMock } from './setupTests';
// @ts-ignore
Firebase.auth = authMock;

describe('<SignOutButton />', () => {
  afterEach(cleanup);

  it('calls Firebase signOut on click', async () => {
    const SignOutButton = (await import('./SignOutButton')).default;
    const { getByText } = render(<SignOutButton />);
    const button = getByText('Sign Out');
    fireEvent.click(button);
    expect(Firebase.auth().signOut).toHaveBeenCalled();
  });
});

带有覆盖率报告的单元测试结果:

 PASS  src/stackoverflow/58562583/SignOutButton.test.tsx
  <SignOutButton />
    ✓ calls Firebase signOut on click (76ms)

  console.log src/stackoverflow/58562583/SignOutButton.tsx:382
    { [Function: mockConstructor]
      _isMockFunction: true,
      getMockImplementation: [Function],
      mock: [Getter/Setter],
      mockClear: [Function],
      mockReset: [Function],
      mockRestore: [Function],
      mockReturnValueOnce: [Function],
      mockResolvedValueOnce: [Function],
      mockRejectedValueOnce: [Function],
      mockReturnValue: [Function],
      mockResolvedValue: [Function],
      mockRejectedValue: [Function],
      mockImplementationOnce: [Function],
      mockImplementation: [Function],
      mockReturnThis: [Function],
      mockName: [Function],
      getMockName: [Function] }

  console.log src/stackoverflow/58562583/SignOutButton.tsx:386
    should keep same reference to authObject: true

-------------------|----------|----------|----------|----------|-------------------|
File               |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files          |    63.64 |      100 |    36.36 |       60 |                   |
 SignOutButton.tsx |      100 |      100 |      100 |      100 |                   |
 firebase.ts       |       25 |      100 |        0 |       25 |             3,4,7 |
 setupTests.ts     |       50 |      100 |    28.57 |    44.44 |        2,3,4,5,11 |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.943s

关于reactjs - 如何模拟 Firebase Auth 方法? ( react ,测试库),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58562583/

相关文章:

javascript - 当状态更新不影响 UI 时防止 "not wrapped in act(...)"Jest 警告

testing - Jest 中的 'toBe' 和 'toEqual' 有什么区别?

javascript - 使用 React.memo 时 Jest Snapshot 错误

reactjs - 将基于类的 React 转换为功能组件

javascript - 我如何通过在 reactJS 的 Material 表中创建新行来访问取消处理程序?

javascript - Firebase云函数: How to push data using wildcards in http requests

javascript - 使 firebase 返回排序后的响应

javascript - 从 0 开始的堆叠条形图 - ChartJS React

javascript - React @flow 避免重复类型

java - 服务器到 Firebase HTTP POST 结果为响应消息 200