使用@testing-library/react-native测试时ref.current.focus()为何失效?
React Native测试:Pressable点击后TextInput的onFocus事件未触发
我在为React Native应用编写测试,组件里的父级Pressable被点击后,会通过ref让TextInput获得焦点。这个功能在Expo中运行正常,但使用@testing-library/react-native测试时,onFocus事件未触发,测试失败。
相关代码
Foo.js
import {Pressable, TextInput} from "react-native"; import {useRef} from "react"; const Foo = (props) => { const inputRef = useRef() return ( <Pressable testID={"pressable"} onPress={() => inputRef.current.focus()}> <TextInput ref={inputRef} testID={"input"} onFocus={props.onFocus} /> </Pressable> ) } export default Foo;
Foo.test.js
import { render, screen, fireEvent } from '@testing-library/react-native'; import Foo from "./foo"; test('onFocus is called after press', () => { const mockFocus = jest.fn(); render(<Foo onFocus={mockFocus} />) // fireEvent(screen.getByTestId("input"), 'focus'); fireEvent.press(screen.getByTestId("pressable")); expect(mockFocus).toHaveBeenCalled() });
测试报错信息
Error: expect(jest.fn()).toHaveBeenCalled() Expected number of calls: >= 1 Received number of calls: 0
解决方案
问题出在@testing-library/react-native对TextInput的focus()方法做了mock处理,测试环境中调用该方法不会自动触发onFocus回调(和真实运行环境逻辑不同)。以下是几种可行的解决方式:
方法一:模拟focus()触发onFocus
用jest.spyOn替换TextInput的focus实现,让它主动调用组件的onFocus回调:
import { render, screen, fireEvent } from '@testing-library/react-native'; import { TextInput } from 'react-native'; import Foo from "./foo"; test('onFocus is called after press', () => { const mockFocus = jest.fn(); // 替换focus方法,触发onFocus回调 const focusSpy = jest.spyOn(TextInput.prototype, 'focus').mockImplementation(function() { this.props.onFocus?.(); }); render(<Foo onFocus={mockFocus} />) fireEvent.press(screen.getByTestId("pressable")); expect(mockFocus).toHaveBeenCalled(); // 恢复原方法,避免影响其他测试 focusSpy.mockRestore(); });
方法二:测试focus()调用逻辑
如果测试核心是验证Pressable点击后会触发TextInput的focus,而非直接验证onFocus,可以调整测试逻辑:
import { render, screen, fireEvent } from '@testing-library/react-native'; import { TextInput } from 'react-native'; import Foo from "./foo"; test('TextInput receives focus after press', () => { const focusSpy = jest.spyOn(TextInput.prototype, 'focus'); render(<Foo />) fireEvent.press(screen.getByTestId("pressable")); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); });
方法三:手动触发focus事件
若仅需验证onFocus逻辑本身,可在Pressable点击后手动触发TextInput的focus事件(适合简单场景,但会绕过组件内的ref.focus()逻辑):
import { render, screen, fireEvent } from '@testing-library/react-native'; import Foo from "./foo"; test('onFocus is called after press', () => { const mockFocus = jest.fn(); render(<Foo onFocus={mockFocus} />) fireEvent.press(screen.getByTestId("pressable")); // 手动触发focus事件 fireEvent.focus(screen.getByTestId("input")); expect(mockFocus).toHaveBeenCalled(); });
内容的提问来源于stack exchange,提问作者Daniel Long
相关产品推荐
相关产品推荐

