React Native中Jest/RTL测试元素样式:toHaveProperty匹配器报错排查
我想了解如何在React Native应用中使用Jest/RTL测试特定元素是否具备特定属性,目前我的toHaveProperty匹配器报错,我认为用法有误。
我有一个React Native的Dummy组件如下:
// Dummy.tsx const Dummy: FC<Props> = ({num, onPressHandler}: Props) => { const [state, setState] = useState(false); return ( <View testID="dummy" style={styles.container}> <Text>abcdef</Text> <Text>{num}</Text> <Button title={'Press'} onPress={onPressHandler} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'black', }, });
我需要测试testID为"dummy"的View组件是否应用了正确样式,于是在测试文件中编写如下代码:
// Dummy.test.tsx describe('dummytest', () => { test('prop', () => { render(<Dummy num={42} onPressHandler={() => {}} />, {}); const element = screen.getByTestId('dummy'); expect(element).toHaveProperty('style', { flex: 1, backgroundColor: 'black', }); }); });
但出现如下错误:
expect(received).toHaveProperty(path, value)
Expected path: "style" Received path: [] Expected value: {"backgroundColor": "black", "flex": 1} Received value: {"_fiber": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": null, "actualDuration": 0, "actualStartTime": -1, "alternate": null, "child": [FiberNode], "childLanes": 0, "deletions": null, "dependencies": null, "elementType": "View", "flags": 0, "index": 0, "key": null, "lanes": 0, "memoizedProps": [Object], "memoizedState": null, "mode": 0, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0, "sibling": null, "stateNode": [Object], "subtreeFlags": 9439749, "tag": 5, "treeBaseDuration": 0, "type": "View", "updateQueue": null}} 7 | 8 | const element = screen.getByTestId('dummy'); > 9 | expect(element).toHaveProperty('style', { | ^ 10 | flex: 1, 11 | backgroundColor: 'black', 12 | }); at Object.toHaveProperty (__tests__/Dummy.test.tsx:9:21)
请问我是否正确使用了toHaveProperty匹配器?
你没有正确使用toHaveProperty匹配器,问题出在两个核心点:
RTL返回的元素是React Fiber节点,不是原生组件实例:
screen.getByTestId('dummy')获取的是React内部的Fiber节点对象,它本身并没有直接的style属性——组件接收的styleprops存储在Fiber节点的_fiber.memoizedProps或_fiber.pendingProps中。StyleSheet.create生成的是特殊引用对象,不能直接对比字面量:
StyleSheet.create会将样式转换为带有内部ID的优化对象,和你写的{flex:1, backgroundColor:'black'}字面量不是同一个引用,直接全等对比会失败。
正确的测试写法
修改测试代码,从Fiber节点的props中获取样式,再用expect.objectContaining做部分匹配(或直接对比预定义的styles.container):
// Dummy.test.tsx import { styles } from '../Dummy'; // 引入组件中的styles describe('dummytest', () => { test('prop', () => { render(<Dummy num={42} onPressHandler={() => {}} />); const element = screen.getByTestId('dummy'); // 从Fiber节点中获取组件接收的props const componentProps = element._fiber.memoizedProps; // 方法1:直接对比预定义的styles.container(适用于静态样式) expect(componentProps.style).toEqual(styles.container); // 方法2:部分匹配样式属性(适用于动态合并的样式) expect(componentProps.style).toEqual(expect.objectContaining({ flex: 1, backgroundColor: 'black', })); }); });
为什么之前的写法报错
toHaveProperty('style')尝试在Fiber节点对象上直接查找style属性,但该对象本身没有这个属性,所以报错"Expected path: 'style', Received path: []"。必须深入到_fiber.memoizedProps中才能拿到组件的style props。
内容的提问来源于stack exchange,提问作者adzahari

