I using Firebase authentication and I would like to test the function using Jest and react-hooks-testing-library.
I have a function that like this:
const loginWithEmailPassword = (email: string, password: string) => {
const auth = getAuth()
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
}).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
}
The signInWithEmailPassword()
function have a then()
and catch()
block.
I have mock the function with this code:
const mockSignUp = jest.fn(() => {
return Promise.resolve({
user: {
uid: "fakeuid",
},
});
})
jest.mock('firebase/auth', () => ({
getAuth: () => mockGetAuth,
signInWithEmailAndPassword: () => mockSignIn,
createUserWithEmailAndPassword: () => mockSignUp
}))
Then I test the function above using react-hooks-testing-library
which like this:
test('Login with Email and Password', () => {
const { result } = renderHook(() => useFirebaseAuth())
const email = 'abc@gmail.com'
const password = '123456'
// here fired my loginWithEmailPassword above
act(() => {
// the problem come from this line
result.current.loginWithEmailPassword(email, password)
})
Then my test failed with this error:
TypeError: (0 , _auth.signInWithEmailAndPassword)(...).then is not a function
46 |
47 | signInWithEmailAndPassword(auth, email, password)
> 48 | .then((userCredential) => {
If I remove the then
block, the test passed. But if I use called the then()
for the function it get the error. I check that my mock for signInWithEmailAndPassword
with return of Promise.resolve()
should be ok, but it still having the error.
I am new in testing field. Please somebody give some suggestion on this and tell me what is wrong with my test? I have absolutely no idea
I have tried to mock it like this, after looking for this answer:
const mockSomething = jest.fn().mockImplementation(() => Promise.resolve({
user: {
uid: "fakeuid",
},
}))
But still having the same error
Finally I able to solve the problem by mock a function with will return promise that resolved with user
like this:
jest.mock('firebase/auth', () => {
return {
getAuth: () => mockGetAuth,
// since this method from firebase return a promise, here need a promise as well
signInWithEmailAndPassword: jest.fn().mockResolvedValue({
user: {
uid: "fakeUid",
},
}),
createUserWithEmailAndPassword: jest.fn().mockResolvedValue({
user: {
uid: "fakeUid",
},
}),
}
})
in the signInWithEmailPassword
, I direct return the Promise
without wrap it with jest.fn()
Then in test, I can do this:
import { signInWithEmailAndPassword, getAuth } from 'firebase/auth';
// here I can check that `signInWithEmailPassword is call with some value
expect(signInWithEmailAndPassword).toBeCalledWith(
getAuth(),
email,
password
);
// Here I can compare my UI to the fakeUid as well
expect(data.user.uid).toEqual('fakeUid')
In test, I have to call signInWithEmailAndPassword
which is direct from firebase/auth
.
Since I already mock the function in jest.mock()
, therefore in test it will return back the fake data I defined directly, which is data: { user: { uid: 'fakeUid' } }
The whole process is like:
firebase/auth
module named signInWithEmailPassword
jest.mock()
test
section, I call the function direct from Firebase, but now instead of "actual" calling it from Firebase, it just return the "fake" Promise
with the fake data that I defined just now.expect(data.user.uid).toEqual('fakeUid')