最近在用 expo 开发 react native 项目,安装配置 jest 后运行 npx jest 之后遇到了这样一个错误:
Expected react-native/jest-preset to define transform[^.+\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$]
react-native/jest-preset contained different transformIgnorePatterns than expected
PASS hooks/useTimeline.test.js
PASS components/__tests__/StyledText-test.js
● Console
console.error
Warning: An update to MonoText inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in MonoText
22 | console.warn(e);
23 | } finally {
> 24 | setLoadingComplete(true);
| ^
25 | SplashScreen.hideAsync();
26 | }
27 | }
at warningWithoutStack (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:131:32)
at warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15835:7)
at loadResourcesAndDataAsync$ (hooks/useCachedResources.ts:24:9)
at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:293:22)
at Generator.next (node_modules/regenerator-runtime/runtime.js:118:21)
at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 1 passed, 1 total
Time: 3.795s
Ran all test suites.
实际上检查 .test.js 文件并没有更新 state 的代码存在:
import * as React from 'react'
import { render } from '@testing-library/react-native'
import { MonoText } from '../StyledText'
it('renders correctly', async () => {
const result = render(<MonoText>Snapshot test!</MonoText>).toJSON()
expect(result).toMatchSnapshot()
})
后来看到了这篇文章:Fix the "not wrapped in act(...)" warning
按照文章作者的说法应该是组件中存在着脱离了 jest 控制的 state 更新,也就是修改 state 的语句被放在了 async 函数或者 timer 函数中,jest 都已经测试完毕开始打扫时才收到了这些语句的调用,查看这里 MonoText 的代码:
import * as React from 'react'
import useCachedResources from '@/hooks/useCachedResources'
import { Text, TextProps } from './Themed'
export const MonoText: React.FC<TextProps> = (props) => {
const isLoadingComplete = useCachedResources()
if (!isLoadingComplete) {
return null
} else {
return <Text {...props} style={[props.style, { fontFamily: 'space-mono' }]} />
}
}
再看这里调用的 useCachedResources:
import { Ionicons } from '@expo/vector-icons'
import * as Font from 'expo-font'
import * as SplashScreen from 'expo-splash-screen'
import * as React from 'react'
export default function useCachedResources() {
const [isLoadingComplete, setLoadingComplete] = React.useState(false)
// Load any resources or data that we need prior to rendering the app
React.useEffect(() => {
/* ↓↓↓ 重点在这里 ↓↓↓ */
async function loadResourcesAndDataAsync() {
try {
SplashScreen.preventAutoHideAsync()
// Load fonts
await Font.loadAsync({
...Ionicons.font,
'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'),
})
} catch (e) {
// We might want to provide this error information to an error reporting service
console.warn(e)
} finally {
setLoadingComplete(true)
SplashScreen.hideAsync()
}
}
loadResourcesAndDataAsync()
}, [])
return isLoadingComplete
}
可以看到应该就是这里的 async 函数 loadResourcesAndDataAsync 导致问题发生,文章作者给出了几种解决方案,这里我选择通过 mock 掉 useCachedResources 来让 jest 感知并控制这个函数:
import * as React from 'react'
import { render } from '@testing-library/react-native'
import { MonoText } from '../StyledText'
/* ↓↓↓ 重点在这里 ↓↓↓ */
jest.mock('@/hooks/useCachedResources')
it('renders correctly', async () => {
const result = render(<MonoText>Snapshot test!</MonoText>).toJSON()
expect(result).toMatchSnapshot()
})
重新运行测试:
Expected react-native/jest-preset to define transform[^.+\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$]
react-native/jest-preset contained different transformIgnorePatterns than expected
PASS hooks/useTimeline.test.js
PASS components/__tests__/StyledText-test.js
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 1 passed, 1 total
Time: 2.961s, estimated 3s
Ran all test suites.
Well done! 🎉