如何用Jest测试自定义StatusBar高度?测试失败问题排查
问题:Jest测试自定义StatusBar高度失败,预期高度与接收值undefined不匹配
我正在用Jest测试iOS和Android双平台下自定义StatusBar的高度,目标是100%测试覆盖率。组件已实现100%覆盖率,但两个高度测试用例失败,报错显示预期高度与接收值(undefined)不匹配。
自定义StatusBar组件代码
import React from 'react'; import { View, Platform, StatusBar, StatusBarStyle, ColorValue, } from 'react-native'; import { getStatusBarHeight } from 'react-native-iphone-x-helper'; type CustomStatusBarProps = { barStyle: StatusBarStyle; backgroundColor: ColorValue; }; export function CustomStatusBar({ barStyle, backgroundColor, }: CustomStatusBarProps) { const STATUS_BAR_HEIGHT = Platform.OS === 'ios' ? getStatusBarHeight() + 20 : 0; console.log('✅ ~ STATUS_BAR_HEIGHT', STATUS_BAR_HEIGHT); return ( <View testID="custom-status-bar" className={`w-full bg-${String( backgroundColor, )} h-[${STATUS_BAR_HEIGHT}]`} > <StatusBar translucent barStyle={barStyle} backgroundColor={backgroundColor} /> </View> ); }
测试运行结果
jest --verbose watchman警告:该监视已重新爬取11次,最近一次原因是:MustScanSubDirs UserDropped。如需解决,请查看文档: 要清除该警告,请运行: `watchman watch-del '/Users/rodrigodiasdefigueiredo/Desktop/zerelparksharing' ; watchman watch-project '/Users/rodrigodiasdefigueiredo/Desktop/zerelparksharing'` PASS src/components/CustomButton/__tests__/CustomButton.test.tsx CustomButton ✓ should render the component (202 ms) ✓ should render the title (2 ms) ✓ should call onpress (2 ms) PASS src/components/CustomFooter/__tests__/CustomFooter.test.tsx CustomFooter ✓ the component rendered (218 ms) ✓ the component rendered (3 ms) console.log ✅ ~ STATUS_BAR_HEIGHT 40 at log (src/components/CustomStatusBar/index.tsx:22:11) console.log ✅ ~ STATUS_BAR_HEIGHT 0 at log (src/components/CustomStatusBar/index.tsx:22:11) console.log ✅ ~ STATUS_BAR_HEIGHT 0 at log (src/components/CustomStatusBar/index.tsx:22:11) FAIL src/components/CustomStatusBar/__tests__/CustomStatusBar.test.tsx CustomStatusBar ✓ should render the component (104 ms) ✓ should the platform be ios (2 ms) ✓ should the platform be android ✕ should render the component with the height 40 for IOS (3 ms) ✕ should render the component with the height 0 for Android (2 ms) ● CustomStatusBar › should render the component with the height 40 for IOS expect(received).toBe(expected) // Object.is equality Expected: 40 Received: undefined 39 | ); 40 | const customStatusBar = getByTestId('custom-status-bar'); > 41 | expect(customStatusBar.props.style[0].height).toBe(40); | ^ 42 | }); 43 | 44 | it('should render the component with the height 0 for Android', () => { at Object.toBe (src/components/CustomStatusBar/__tests__/CustomStatusBar.test.tsx:41:51) ● CustomStatusBar › should render the component with the height 0 for Android expect(received).toBe(expected) // Object.is equality Expected: 0 Received: undefined 50 | ); 51 | const customStatusBar = getByTestId('custom-status-bar'); > 52 | expect(customStatusBar.props.style[0].height).toBe(0); | ^ 53 | }); 54 | }); 55 | at Object.toBe (src/components/CustomStatusBar/__tests__/CustomStatusBar.test.tsx:52:51) ----------------------------|---------|----------|---------|---------|------------------- | 文件 | % 语句覆盖率 | % 分支覆盖率 | % 函数覆盖率 | % 行覆盖率 | 未覆盖行号 | |--------------------------|-------------|--------------|-------------|-------------|------------------| | 所有文件 | 7.03 | 4.34 | 8.88 | 7.14 | | | components/CustomButton | 100 | 100 | 100 | 100 | | | index.tsx | 100 | 100 | 100 | 100 | | | components/CustomFooter | 100 | 100 | 100 | 100 | | | index.tsx | 100 | 100 | 100 | 100 | | | components/CustomStatusBar| 100 | 100 | 100 | 100 | | | index.tsx | 100 | 100 | 100 | 100 | | | components/CustomToast | 0 | 0 | 0 | 0 | | | index.tsx | 0 | 0 | 0 | 0 | 12-64 | | components/OpenDoorModal | 0 | 100 | 0 | 0 | | | index.tsx | 0 | 100 | 0 | 0 | 17-62 | | screens/Home | 0 | 0 | 0 | 0 | | | index.tsx | 0 | 0 | 0 | 0 | 11-91 | | screens/Login | 0 | 100 | 0 | 0 | | | index.tsx | 0 | 100 | 0 | 0 | 9-13 | | screens/Menu | 0 | 100 | 0 | 0 | | | index.tsx | 0 | 100 | 0 | 0 | 4-5 | | screens/MyAccess | 0 | 0 | 0 | 0 | | | index.tsx | 0 | 0 | 0 | 0 | 5-16 | | screens/OnBoarding | 0 | 0 | 0 | 0 | | | index.tsx | 0 | 0 | 0 | 0 | 24-180 | | slides.ts | 0 | 0 | 0 | 0 | | | screens/Vehicles | 0 | 100 | 0 | 0 | | | index.tsx | 0 | 100 | 0 | 0 | 4-5 | | screens/Wallet | 0 | 100 | 0 | 0 | | | index.tsx | 0 | 100 | 0 | 0 | 4-5 | | slices/counter | 0 | 100 | 0 | 0 | | | counterSlice.ts | 0 | 100 | 0 | 0 | 9-34 | ----------------------------|---------|----------|---------|---------|------------------- 测试套件:1个失败,2个通过,共3个 测试用例:2个失败,8个通过,共10个 快照:共0个 耗时:2.343秒 已运行所有测试套件。
问题原因
测试代码中尝试通过customStatusBar.props.style[0].height获取高度,但组件使用className(Tailwind CSS类)设置高度,而非直接传入style属性,因此style数组中没有对应的height值,导致返回undefined。
解决方案
方案1:修改测试逻辑,验证className中的高度值
直接检查组件的className是否包含对应的高度类:
// iOS测试用例 it('should render the component with the height 40 for IOS', () => { jest.mock('react-native-iphone-x-helper', () => ({ getStatusBarHeight: () => 20, // 模拟返回20,加20后得到40 })); Platform.OS = 'ios'; render(<CustomStatusBar barStyle="dark-content" backgroundColor="black" />); const customStatusBar = getByTestId('custom-status-bar'); expect(customStatusBar.props.className).toContain('h-[40]'); }); // Android测试用例 it('should render the component with the height 0 for Android', () => { Platform.OS = 'android'; render(<CustomStatusBar barStyle="dark-content" backgroundColor="black" />); const customStatusBar = getByTestId('custom-status-bar'); expect(customStatusBar.props.className).toContain('h-[0]'); });
方案2:组件改用style属性设置高度(可选)
如果更倾向于通过style属性测试,可以修改组件代码:
return ( <View testID="custom-status-bar" className={`w-full bg-${String(backgroundColor)}`} style={{ height: STATUS_BAR_HEIGHT }} > <StatusBar translucent barStyle={barStyle} backgroundColor={backgroundColor} /> </View> );
此时原测试逻辑即可生效,无需修改测试代码。
额外注意事项
- 测试前需要正确模拟
react-native-iphone-x-helper的getStatusBarHeight方法,确保iOS下计算出的高度符合预期。 - 切换Platform.OS后,建议在测试结束后恢复初始值,避免影响其他测试:
afterEach(() => { jest.restoreAllMocks(); Platform.OS = 'ios'; // 或项目初始默认值 });
内容的提问来源于stack exchange,提问作者Rodrigo Dias
相关产品推荐
相关产品推荐

