如何用Jest与React Native Testing Library模拟外部函数测试组件?
问题描述
我有一个调用外部函数的React Native组件,代码如下:
// AComponent.js import { Text, View } from "react-native"; import { getAString } from "./utils/function"; const AComponent = () => { return ( <View> <Text>{getAString()}</Text> </View> ); }; export default AComponent;
外部函数getAString定义在单独文件中:
// function.js export function getAString(): string { return "This comes from GetAString"; }
尝试测试AComponent时模拟这个函数,写了如下测试代码:
var AComponent = require("../src/AComponent").default; import { getAString } from "../src/utils/function"; import { render, screen, fireEvent } from "@testing-library/react-native"; describe("A Component's test", () => { it("mocking the function", async () => { render(<AComponent />); jest.spyOn(AComponent, "getAString").mockImplementation(() => { return "This is a mocked result"; }); const element = screen.getByText("This is a mocked result"); expect(element).toBeTruthy(); }); });
但测试运行失败,报错:
Cannot spy on the getAString property because it is not a function; undefined given instead
推测是测试代码错误地认为getAString是AComponent的属性,但实际并非如此,请问正确的模拟方法是什么?
正确的模拟方法
要解决这个问题,得直接模拟getAString所在的模块,而非尝试在组件实例上查找该函数,以下是两种常用的正确实现方式:
方法一:使用jest.spyOn模拟模块导出的函数
import AComponent from "../src/AComponent"; import * as functionModule from "../src/utils/function"; import { render, screen } from "@testing-library/react-native"; describe("A Component's test", () => { it("mocks the external function correctly", () => { // 先模拟函数,再渲染组件 const mockGetAString = jest.spyOn(functionModule, "getAString") .mockReturnValue("This is a mocked result"); render(<AComponent />); expect(screen.getByText("This is a mocked result")).toBeTruthy(); // 验证函数被调用 expect(mockGetAString).toHaveBeenCalled(); // 测试结束后恢复原函数,避免影响其他测试 mockGetAString.mockRestore(); }); });
核心要点:
- 将整个
function模块导入为functionModule,这样就能用jest.spyOn监听模块导出的getAString函数 - 必须先完成函数模拟,再渲染组件,否则组件会优先执行原函数逻辑
- 用
mockRestore()在测试完成后恢复原函数,保证不同测试用例之间的隔离性
方法二:使用jest.mock直接Mock整个模块
如果需要在多个测试用例中重复使用同一模拟逻辑,可以直接Mock整个模块:
import AComponent from "../src/AComponent"; import { getAString } from "../src/utils/function"; import { render, screen } from "@testing-library/react-native"; // 全局Mock整个function模块,该代码会自动提升到文件顶部执行 jest.mock("../src/utils/function", () => ({ getAString: jest.fn(() => "This is a mocked result") })); describe("A Component's test", () => { it("uses the mocked function", () => { render(<AComponent />); expect(screen.getByText("This is a mocked result")).toBeTruthy(); expect(getAString).toHaveBeenCalled(); }); it("can override the mock for a single test", () => { // 临时修改Mock的返回值 getAString.mockReturnValue("Another mocked value"); render(<AComponent />); expect(screen.getByText("Another mocked value")).toBeTruthy(); }); });
核心要点:
jest.mock会被Jest自动提升到文件顶部执行,确保组件渲染时已经使用了Mock后的函数- 可以在单个测试用例中,通过
mockReturnValue或mockImplementation临时修改Mock的返回值 - 所有测试结束后Jest会自动清理这些Mock,无需手动恢复
内容的提问来源于stack exchange,提问作者Rafael
相关产品推荐
相关产品推荐

