如何正确模拟利用本机代码的 React Native 模块?



我正在用TypeScript构建一个React Native应用程序。我正在使用 react-native-firebase 来发送通知。此外,我还使用Jest和Enzyme进行单元测试。

我有以下包装器函数来检查用户权限:

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);
}
});
});

现在我想测试函数是否被调用。

这是我写的测试:

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);
});
});

这将引发错误:

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`.

所以在我看来,使用本机代码的模块不能简单地自动模拟。

所以我试图手动模拟它。在与node_modules相邻的根项目中的文件夹__mocks__内,我创建了一个名为react-native-firebase.ts的文件,如下所示:

const firebase = {
messaging: jest.fn(() => ({
hasPermission: jest.fn(() => new Promise(resolve => resolve(true)))
}))
};
export default firebase;

但是这段代码也失败了,因为据称firebase.messaging是未定义的。

如何测试这些东西? 😣

编辑:哇,在RN 0.57.x:(中,嘲笑似乎完全被打破

这是一种测试依赖于react-native-firebase的代码的方法

只需在__mocks__/react-native-firebase.js中添加一个空文件即可创建手动模拟,模拟文件夹应与node_modules处于同一级别,如 jest 文档中特别是模拟节点模块部分所述。

使用此手动模拟,您可以避免错误

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`.

那么你不需要jest.mock("react-native-firebase");也不需要 测试expect(firebase.messaging().hasPermission).toHaveBeenCalledTimes(1);

相反,您可以做的是隔离依赖于小函数react-native-firebase的代码,然后监视那些添加已知结果的代码。

例如,这个依赖于 react-native-firebase 的 verifyPhone 函数。

// ./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;

要测试依赖于verifyPhoneNumber函数的代码,您需要监视它并像这样替换其实现。

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();
});
});

我希望它有所帮助!

最新更新