问题
I'm building a React Native app with TypeScript. I'm using react-native-firebase for my notifications. I'm furthermore using Jest and Enzyme for my unit tests.
I have the following wrapper function to check the users permissions:
export const checkPermissions = (): Promise<boolean> =>
new Promise((resolve, reject) => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
resolve(enabled);
} else {
firebase
.messaging()
.requestPermission()
.then(resolve)
.catch(reject);
}
});
});
Now I want to test if the function gets called.
Here is the test I wrote:
import * as firebase from "react-native-firebase";
import { checkPermissions } from "./notificationHelpers";
jest.mock("react-native-firebase");
describe("checkPermissions", () => {
beforeEach(async done => {
jest.resetAllMocks();
await checkPermissions();
done();
});
it("should call firebase.messaging().hasPermission()", () => {
expect(firebase.messaging().hasPermission).toHaveBeenCalledTimes(1);
});
});
This throws the error:
FAIL app/services/utils/core/notificationHelpers/notificationHelpers.test.ts
● Test suite failed to run
RNFirebase core module was not found natively on iOS, ensure you have correctly included the RNFirebase pod in your projects `Podfile` and have run `podinstall`.
See http://invertase.link/ios for the ios setup guide.
Error: RNFirebase core module was not found natively on iOS, ensure you have correctly included the RNFirebase pod in your projects `Podfile` and haverun `pod install`.
So it seems to me that modules that use native code can't simply by auto-mocked.
So I tried to manually mock it. Inside a folder __mocks__
that's within my root project adjacent to node_modules
I created a file called react-native-firebase.ts
, which looks like this:
const firebase = {
messaging: jest.fn(() => ({
hasPermission: jest.fn(() => new Promise(resolve => resolve(true)))
}))
};
export default firebase;
But this code also fails, because firebase.messaging
is allegedly undefined.
How would one test this stuff? 😣
EDIT: Wow, mocking seems to be completely broken in RN 0.57.x :(
回答1:
Here is a way you can test code that depends on react-native-firebase
Create a manual mock just by adding an empty file in __mocks__/react-native-firebase.js
, mocks folder should be at the same level as node_modules
as explained in jest docs specifically Mocking Node modules section.
With this manual mock you avoid the error
RNFirebase core module was not found natively on iOS, ensure you have correctly included the RNFirebase pod in your projects `Podfile` and have run `pod install`.
Then you don't need jest.mock("react-native-firebase");
also you don't need to
test expect(firebase.messaging().hasPermission).toHaveBeenCalledTimes(1);
What you can do instead is isolate the code that depends on react-native-firebase
on small functions and then spy on those adding known results.
For example, this verifyPhone function that depends on react-native-firebase.
// ./lib/verifyPhoneNumber.ts
import firebase from "react-native-firebase";
const verifyPhoneNumber = (phoneNumber: string) => {
return new Promise((resolve, reject) => {
firebase
.auth()
.verifyPhoneNumber(phoneNumber)
.on("state_changed", phoneAuthSnapshot => {
resolve(phoneAuthSnapshot.state);
});
});
};
export default verifyPhoneNumber;
To test code that depends on verifyPhoneNumber
function you spy it and replace its implementation like this.
jest.mock("react-native-firebase");
import { signInWithPhone } from "../actions";
import verifyPhoneNumberEpic from "./verifyPhoneNumberEpic";
import { ActionsObservable } from "redux-observable";
// Note "import * as" is needed to use jest.spyOn(verifyPhoneNumber, "default");
import * as verifyPhoneNumber from "./lib/verifyPhoneNumber";
describe("Epic: verifyPhoneNumberEpic", () => {
test('On Request "[Auth] Verify Phone Number Request" executes the promise', done => {
// Create the spy
const verifyPhoneNumberMock = jest.spyOn(verifyPhoneNumber, "default");
// For this test I simulate the promise resolves with CODE_SENT
verifyPhoneNumberMock.mockImplementation(async () => "CODE_SENT");
// the rest of the code is very specific for testing Epics (redux-observable)
// But all you need to know is that internally my epic verifyPhoneNumberEpic
// calls verifyPhoneNumber but the implication will be replaced with the spy
const requestAction = signInWithPhone.request({
phoneNumber: "+40111222333"
});
const action$ = ActionsObservable.of(requestAction);
verifyPhoneNumberEpic(action$, null, null).subscribe((action: any) => {
expect(action.payload.code).toBe("CODE_SENT");
expect(action.type).toBe(signInWithPhone.success.toString());
done();
});
});
I hope it helps!
来源:https://stackoverflow.com/questions/52815957/how-to-correctly-mock-a-react-native-module-that-utilizes-native-code