React Native 测试 - 无需等待即可行动
Posted
技术标签:
【中文标题】React Native 测试 - 无需等待即可行动【英文标题】:React Native testing - act without await 【发布时间】:2021-03-05 05:24:58 【问题描述】:以下测试通过,但我两次收到以下警告,我不知道为什么。有人可以帮我弄清楚吗?
console.error
Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);
at printWarning (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:120:30)
at error (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:92:5)
at ../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14953:13
at tryCallOne (../../node_modules/react-native/node_modules/promise/lib/core.js:37:12)
at ../../node_modules/react-native/node_modules/promise/lib/core.js:123:15
at flush (../../node_modules/asap/raw.js:50:29)
import fireEvent from '@testing-library/react-native'
import renderScreen from 'test/render'
describe('screens/home', () =>
it('should render and redirect to the EventScreen', async () =>
const
getByA11yLabel,
findByA11yLabel,
findAllByA11yLabel,
toJSON
= renderScreen('Main')
expect(toJSON()).toMatchSnapshot('Default render')
const title = 'New event'
const titleInput = getByA11yLabel('event.title')
// Change title - sync fn
fireEvent.changeText(titleInput, title)
// Create button should be visible
const createButton = await findByA11yLabel('event.create')
expect(titleInput.props.value).toBe(title)
expect(createButton).toBeTruthy()
expect(toJSON()).toMatchSnapshot('Change title')
// Create event - async fn
fireEvent.press(createButton)
// The app should be redirected to the EventScreen
const titleInputs = await findAllByA11yLabel('event.title')
const upsertButton = await findByA11yLabel('event.upsert')
expect(toJSON()).toMatchSnapshot('Create event')
expect(titleInputs).toHaveLength(2)
expect(titleInputs[0].props.value).toBe('') // @MainScreen
expect(titleInputs[1].props.value).toBe(title) // @EventScreen
expect(upsertButton).toBeTruthy()
)
)
据我所知,没有必要用act
-link 包裹fireEvent
findBy*
也自动用 act
包裹 - link
相关issue in GitHub 仍然开放
依赖关系:
反应:16.13.1 世博会:39.0.4 开玩笑:26.6.3 ts-jest:26.4.4 jest-expo: 39.0.0 @testing-library/jest-native: 3.4.3 @testing-library/react: 11.2.2 @testing-library/react-native: 7.1.0 反应测试渲染器:16.13.1 打字稿:4.1.2【问题讨论】:
【参考方案1】:如果您已经用尽所有其他调试工作并且非常确定您的代码编写正确,则可能与 react-native/jest-preset
用模拟替换 global.Promise
相关(请参阅 issue)。
在这种情况下,问题的解决方案是覆盖/修补 jest 预设以首先保存原始全局 Promise,应用 react-native/jest-preset
然后恢复原始 Promise(覆盖模拟版本)。这让我可以在与渲染无关的测试中使用await
,而不会触发可怕的
console.error
Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);
此 sn-p 显示了执行此修补程序的一种方法:https://github.com/sbalay/without_await/commit/64a76486f31bdc41f5c240d28263285683755938
【讨论】:
这听起来不错,但我正在使用 jest-expo,这个解决方案对我没有帮助。【参考方案2】:我也遇到了同样的问题。对于我的情况,我在我的组件中使用了useEffect
。在测试时,它提示我将渲染包装在 act()
调用中。一旦我这样做了,即act(async () => ...)
,我最初的问题就解决了,但我得到了上述错误(Warning: You called act(async () => ...) without await.
)。我不得不在我的测试中使用await act(async () => ...)
来解决这个问题。虽然我仍然不确定为什么需要它。
作为参考,我正在使用await act(async () => ...);
添加一个完整的示例组件和相应的测试
LocationComponent.tsx
/** @jsx jsx */
import jsx from 'theme-ui';
import FunctionComponent, useEffect, useState from 'react';
type Coordinate =
latitude: number;
longitude: number;
;
const LocationComponent: FunctionComponent<any> = () =>
const [coordinate, setCoordinate] = useState<Coordinate>();
const [sharedLocation, setSharedLocation] = useState<boolean>();
useEffect(() =>
let mounted = true;
if (!coordinate && navigator)
navigator.geolocation.getCurrentPosition(function (position)
setCoordinate(
latitude: position.coords.latitude,
longitude: position.coords.longitude,
);
);
navigator.permissions
.query( name: 'geolocation' )
.then(function (result)
if (mounted) setSharedLocation(result.state === 'granted');
);
return () => (mounted = false);
);
return (
<>
<div>Location shared:sharedLocation ? 'Yes' : 'No'</div>
<div>Latitude:coordinate?.latitude</div>
<div>Longitude:coordinate?.longitude</div>
</>
);
;
export default LocationComponent;
LocationComponent.spec.tsx
import React from 'react';
import render, waitFor from '@testing-library/react';
import act from 'react-dom/test-utils';
import LocationComponent from '../../../../../src/components/scheduler/location/LocationComponent';
const TEST_COORDS =
latitude: 41.8817089,
longitude: -87.643301,
;
global.navigator.permissions =
query: jest
.fn()
.mockImplementationOnce(() => Promise.resolve( state: 'granted' )),
;
global.navigator.geolocation =
getCurrentPosition: jest.fn().mockImplementationOnce((success) =>
Promise.resolve(
success(
coords: TEST_COORDS,
)
)
),
;
describe("Location Component when location share is 'granted'", () =>
it('should display current location details', async () =>
await act(async () =>
const getByText = render(<LocationComponent />);
/*expect(
await waitFor(() => getByText('Location shared:Yes'))
).toBeInTheDocument();*/
expect(
await waitFor(() => getByText('Latitude:41.8817089'))
).toBeInTheDocument();
expect(
await waitFor(() => getByText('Longitude:-87.643301'))
).toBeInTheDocument();
);
);
);
【讨论】:
以上是关于React Native 测试 - 无需等待即可行动的主要内容,如果未能解决你的问题,请参考以下文章
react native 入门 - 环境搭建, 创建第一个Hello World