【问题标题】:Mocking data returned by useQuery using MockedProvider使用 MockedProvider 模拟 useQuery 返回的数据
【发布时间】:2021-01-27 13:29:25
【问题描述】:

我有一个简单的组件。它所做的只是使用 useQuery 获取数据并将其传递给另一个组件。该组件工作正常,但如果不添加此处所述的 hack,我将无法对其进行测试:

Testing Final Stage

我看过MockedProvider requires timeout,这是在 2.5 年前被问到的。有没有其他可用的方法?我不敢相信图书馆团队提倡将 wait/setTimeout 放在单位中!

组件:

export const PROFILE_QUERY = gql`
  query {
    profile {
      roles
    }
  }
`;

export const Connected = () => {
  const { loading, data, error } = useQuery(PROFILE_QUERY);
  const setCurrentProfile = (role: string) => {
    cachedSettings(getSettings(role));
  };
  const roles = data?.profile?.roles;
  return <Profile {...{ roles, loading, error, setCurrentProfile }} />;
};

测试用例:

import React from 'react';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import Profile from '../Profile';
import ConnectedProfile from '..';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';
import { PROFILE_QUERY } from '../Profile.connected';

jest.mock('../Profile', () => {
  return jest.fn(() => null);
});

describe('Connected <Profile />', () => {
  const renderComponent = (roles: string[] | undefined) => {
    const mock: MockedResponse = {
      request: {
        query: PROFILE_QUERY
      },
      result: {
        data: {
          profile: {
            roles
          }
        }
      }
    };

    return render(
        <MockedProvider mocks={[mock]} addTypename={false}>
          <MemoryRouter>
            <ConnectedProfile />
          </MemoryRouter>
        </MockedProvider>
    );
  };

  it('multiple roles must have been passed', async () => {
    renderComponent(['foo', 'bar']);
    **//DOCUMENTATION  says do following? - will have to wrap rendering in act as well
    //await new Promise(resolve => setTimeout(resolve, 0));**
    const args = (Profile as jest.Mock).mock.calls[0][0];
    expect(args.roles).toEqual(['agent', 'administrator']);
  });

  it('no roles passed', async () => {
    renderComponent(undefined);
    //DOCUMENTATION says do following?
    //await new Promise(resolve => setTimeout(resolve, 0));
    const args = (Profile as jest.Mock).mock.calls[0][0];
    expect(args.roles).toEqual(undefined);
  });

  afterEach(() => (Profile as jest.Mock).mockClear());
});

【问题讨论】:

  • 如果 MockedProvider 像那样工作,则需要延迟。测试库鼓励使用 waitFor 以不依赖于实现。
  • WaitFor 是一个很好的策略,但在我的情况下,我真的不想测试由子组件呈现的元素,我只想测试正在传递的道具。
  • waitFor 不限于元素,它适用于任何异步断言。

标签: reactjs unit-testing jestjs apollo-client react-testing-library


【解决方案1】:

如果MockedProvider 是异步的并且包含一个滴答声延迟,则要求至少延迟相同的时间量是合理的。这是异步测试的常见问题,这也是流行的 flush-promises 实用程序所做的。

React 测试库专注于黑盒测试,并鼓励使用 waitFor 助手而不依赖于实现。由于轮询间隔,可能会导致累积测试时间:

renderComponent(undefined);
await waitFor(() => {
  expect(Profile).toBeCalledTimes(1);
  expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));
});

由于这种MockedProvider 行为是已知的并记录在案的,因此可以在renderComponent 助手本身中安全地实现它:

const renderComponent = async (roles: string[] | undefined) => {
    ...
    const result = render(
        <MockedProvider mocks={[mock]} addTypename={false}>...</MockedProvider>
    );
    await new Promise(resolve => setTimeout(resolve));
    return result;
};

...
await renderComponent(undefined);
expect(Profile).toBeCalledTimes(1);
expect(Profile).toBeCalledWith(expect.objectContaining({ roles: undefined }));

【讨论】:

    猜你喜欢
    • 2021-02-16
    • 1970-01-01
    • 2021-11-12
    • 2021-02-05
    • 2022-10-26
    • 2020-10-30
    • 2021-07-09
    • 2020-09-21
    • 1970-01-01
    相关资源
    最近更新 更多