Reputation: 677
I have the following component:
import React from "react";
import Firebase from "../../Firebase";
const SignOutButton = () => (
<button type="button" onClick={() => Firebase.auth().signOut()}>
Sign Out
</button>
);
export default SignOutButton;
I want to test that Firebase.auth().signOut
is called onClick
.
I have found this mock of Firebase.auth
elsewhere:
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 };
In SignOutButton.test I have:
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();
});
});
My test results in expected calls being 1 but receiving 0.
What am I doing wrong?
Thank you!
Upvotes: 3
Views: 5119
Reputation: 102207
Your tests have the following issues:
You should import SignOutButton
component after replacing the Firebase.auth
method with authMock
. Otherwise, the Firebase.auth
method is the original version rather than the mocked version. You can check using console.log(Firebase.auth)
.
You should return the same reference for the returned value of Firebase.auth
method. Otherwise, the assertions will fail.
The complete unit test solution:
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();
});
});
unit test result with coverage report:
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
Upvotes: 3