React Native单元测试遇TypeError:无法读取null的props属性
React Native单元测试报错:TypeError: Cannot read properties of null (reading 'props')
我使用Jest和@testing-library/react-native为React Native应用编写单元测试时,遇到了TypeError: Cannot read properties of null (reading 'props')错误。尝试用getByTestId替代queryByTestId无效,console.log(swiper)返回null,希望解决该问题。
组件代码
import { Button, StyleSheet, Text, View } from 'react-native' import Swiper from "react-native-deck-swiper"; import React, { Component } from "react"; import { styles } from "./StyledSwipe.js"; function* range(start, end) { for (let i = start; i <= end; i++) { yield i; } } export default class Exemple extends Component { constructor(props) { super(props); this.state = { cards: [...range(1, 50)], swipedAllCards: false, swipeDirection: "", cardIndex: 0, }; this.swiper = {}; } renderCard = (card, index) => { return ( <View style={styles.card}> <Text style={styles.text}> {card} </Text> </View> ); }; onSwiped = (type) => { console.log(`on swiped ${type}`); }; onSwipedAllCards = () => { this.setState({ swipedAllCards: true, }); }; swipeLeft = () => { this.swiper.swipeLeft(); }; render() { return ( <View style={styles.container}> <Swiper testID="swipe" ref={(swiper) => { this.swiper = swiper; }} onSwiped={() => this.onSwiped("general")} onSwipedLeft={() => this.onSwiped("left")} onSwipedRight={() => this.onSwiped("right")} onSwipedTop={() => this.onSwiped("top")} onSwipedBottom={() => this.onSwiped("bottom")} onTapCard={this.swipeLeft} cards={this.state.cards} cardIndex={this.state.cardIndex} cardVerticalMargin={80} renderCard={this.renderCard} onSwipedAll={this.onSwipedAllCards} stackSize={3} stackSeparation={15} overlayLabels={{ bottom: { title: "BLEAH", style: { label: { backgroundColor: "black", borderColor: "black", color: "white", borderWidth: 1, }, wrapper: { flexDirection: "column", alignItems: "center", justifyContent: "center", }, }, }, left: { title: "NOPE", style: { label: { backgroundColor: "black", borderColor: "black", color: "white", borderWidth: 1, }, wrapper: { flexDirection: "column", alignItems: "flex-end", justifyContent: "flex-start", marginTop: 30, marginLeft: -30, }, }, }, right: { title: "LIKE", style: { label: { backgroundColor: "black", borderColor: "black", color: "white", borderWidth: 1, }, wrapper: { flexDirection: "column", alignItems: "flex-start", justifyContent: "flex-start", marginTop: 30, marginLeft: 30, }, }, }, top: { title: "SUPER LIKE", style: { label: { backgroundColor: "black", borderColor: "black", color: "white", borderWidth: 1, }, wrapper: { flexDirection: "column", alignItems: "center", justifyContent: "center", }, }, }, }} animateOverlayLabelsOpacity animateCardOpacity swipeBackCard > <Button testID="tillbaka" style={styles.backButton} onPress={() => this.swiper && this.swiper.swipeBack()} title="Tillbaka" /> </Swiper> </View> ); } }
测试代码
import React from 'react'; import { Button, Text } from 'react-native'; import { shallow, configure } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; import { render, fireEvent, waitFor, getByText, getByTestId, queryByTestId} from '@testing-library/react-native'; import Exemple, {onTapCard, onSwiped, swipeBack, swipeLeft} from './Swipe.js'; import { act } from 'react-dom/test-utils'; import Swiper from 'react-native-deck-swiper'; describe('Exemple component', () => { test("logs swipe direction", () => { const spy = jest.spyOn(console, "log"); const { queryByTestId } = render(<Exemple/>); const swiper = queryByTestId("swiper"); console.log(swiper) fireEvent(swiper, "onSwipedLeft"); expect(spy).toHaveBeenCalledWith("on swiped left"); spy.mockClear(); expect(swiper.props.swipedAllCards).toBe(true); }); });
报错信息
Exemple component › logs swipe direction TypeError: Cannot read properties of null (reading 'props') 18 | console.log(swiper) 19 | > 20 | fireEvent(swiper, "onSwipedLeft"); | ^ 21 | 22 | expect(spy).toHaveBeenCalledWith("on swiped left"); 23 | at props (node_modules/@testing-library/react-native/src/fireEvent.ts:95:22) at getEventHandler (node_modules/@testing-library/react-native/src/fireEvent.ts:79:19) at findEventHandler (node_modules/@testing-library/react-native/src/fireEvent.ts:112:19) at invokeEvent (node_modules/@testing-library/react-native/src/fireEvent.ts:143:12) at Object.<anonymous> (src/Components/Swipe/Swipe.test.js:20:14)
解决方案
1. 修正TestID匹配错误
组件中Swiper的testID是"swipe",但测试代码中使用queryByTestId("swiper")查找元素,直接导致找不到元素返回null。把测试代码中的查找语句改为:
const swiper = getByTestId("swipe");
推荐使用getByTestId替代queryByTestId,因为getByTestId会在找不到元素时直接抛出错误,更利于排查问题。
2. 修正fireEvent触发事件的方式
fireEvent(swiper, "onSwipedLeft")是错误的写法,@testing-library/react-native中触发组件事件时,不需要加on前缀,正确的写法是:
fireEvent(swiper, "swipeLeft");
3. 修正错误的断言逻辑
swipedAllCards是Exemple组件的state属性,并非Swiper组件的props,测试中expect(swiper.props.swipedAllCards).toBe(true)完全错误。可以通过组件实例获取state,或者模拟滑完所有卡片后验证状态。
修改后的测试代码示例
import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react-native'; import Exemple from './Swipe.js'; describe('Exemple component', () => { test("logs swipe direction when swiped left", () => { // 模拟console.log避免污染测试输出 const spy = jest.spyOn(console, "log").mockImplementation(); const { getByTestId } = render(<Exemple/>); const swiper = getByTestId("swipe"); // 正确触发左滑事件 fireEvent(swiper, 'swipeLeft'); expect(spy).toHaveBeenCalledWith("on swiped left"); // 恢复console.log spy.mockRestore(); }); test("sets swipedAllCards to true after swiping all cards", async () => { const { getByTestId, instance } = render(<Exemple/>); const swiper = getByTestId("swipe"); // 模拟滑完所有50张卡片 for (let i = 0; i < 50; i++) { fireEvent(swiper, 'swipeLeft'); } // 等待state更新完成 await waitFor(() => { expect(instance.state.swipedAllCards).toBe(true); }); }); });
额外优化点
- 移除测试代码中未使用的enzyme相关导入,减少冗余
- 不需要导入组件中未使用的方法(如
onTapCard、swipeBack等) - 使用
mockImplementation和mockRestore处理console.log,避免测试输出混乱
内容的提问来源于stack exchange,提问作者ems
相关产品推荐
相关产品推荐

