【问题标题】:React Native testing - act without awaitReact Native 测试 - 无需等待即可行动
【发布时间】: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

【问题讨论】:

    标签: react-native react-native-testing-library


    【解决方案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();
        });
      });
    });
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-24
      • 2015-02-19
      • 2015-08-25
      • 2012-07-12
      • 1970-01-01
      相关资源
      最近更新 更多