You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:18:24