如何用Jest模拟同一属性下的不同对象?(Firebase React场景)
auth property that is both a function and an object in Jest? Problem Description
I'm using Firebase in a React component, and the auth property on the Firebase object acts as both a function (when calling auth()) and an object (to access properties like GoogleAuthProvider). Here's my setup:
/App.js
import React, { useEffect } from 'react'; import { firebase } from './be/firebase'; function App() { useEffect(() => { console.log('@App'); console.log(firebase.auth.GoogleAuthProvider.PROVIDER_ID); firebase.auth().onAuthStateChanged(/* stuff */); }, []); return <div></div>; } export default App;
/be/firebase.js
import * as firebase from 'firebase/app'; import 'firebase/auth'; const firebaseConfig = { /* my config */ }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig); } export { firebase };
The code works fine in production, but I'm struggling to mock this dual-behavior auth property in Jest. My initial mock attempt looks like this:
/App.test.js
jest.mock('./be/firebase', () => { return { firebase: { auth: { GoogleAuthProvider: { PROVIDER_ID: 'mock', }, onAuthStateChanged: jest.fn(), }, } }; });
This throws an error: TypeError: _firebase.firebase.auth is not a function because my mock only treats auth as an object, not a function that can be called.
Solution
Ah, I see the issue here! Firebase's auth is tricky because it's both a function (when you call auth()) and an object that holds static properties like GoogleAuthProvider directly on it. Your mock needs to replicate this dual behavior. Here's the fix:
/App.test.js
jest.mock('./be/firebase', () => { // 1. Create a mock auth function that returns an auth instance object const mockAuth = jest.fn(() => ({ onAuthStateChanged: jest.fn() })); // 2. Attach the static GoogleAuthProvider property directly to the mock function mockAuth.GoogleAuthProvider = { PROVIDER_ID: 'mock' }; return { firebase: { auth: mockAuth } }; });
Explanation
- We first define
mockAuthas a Jest mock function. When you callfirebase.auth()in your component, this function will return an object with the mockedonAuthStateChangedmethod — just like the real Firebase auth instance. - Then we add the
GoogleAuthProviderproperty directly to themockAuthfunction itself. This lets you accessfirebase.auth.GoogleAuthProviderexactly as you do in production code. - This setup perfectly mirrors Firebase's actual structure, where
authis callable (to get the auth instance) and also hosts static properties.
If you need to mock other auth-related features later, just extend this pattern: attach static properties to the mockAuth function, and add instance methods to the object it returns.
内容的提问来源于stack exchange,提问作者Petro Ivanenko

