【问题标题】:Apollo MockedProvider always returns undefined for data field, even after loadingApollo MockedProvider 总是返回未定义的数据字段,即使在加载后也是如此
【发布时间】:2021-10-14 09:08:09
【问题描述】:

一些背景知识:我有一个在加载时立即调用 useQuery 挂钩的组件。在该查询运行时,我旋转了一个加载微调器。完成后,我会根据数据渲染内容。

我添加了一个useEffect 挂钩,用于监视查询结果并记录数据,这就是我观察到此问题的方式。

为了简化,它的工作方式如下:

export default function MyComponent(props: ???) {
    const result = useQuery(INITIAL_DATA_QUERY, { variables: { id: 1 } });

    React.useEffect(() => console.log(JSON.stringify({
        loading: result.loading,
        data: result.data,
        error: result.error,
    })), [result]);

    if (result.loading) return <LoadingScreen message="Fetching data..."/>;
    else if (result.error) return <ErrorPage/>
    else return <Stuff info={result.data}> // omitted because it's unimportant to the issue
}

当我在野外运行这个组件时,一切都按预期工作。它通过 Apollo 使用 GraphQL 到达端点,根据结果做出渲染决策等。

当我尝试模拟请求时,result.dataresult.error 字段永远不会改变,即使 result.loading 字段会改变。我正在使用react-testing-library 运行测试。

我的测试如下所示:

it("should load the data then render the page", () => {

    const mocks = [{
        request: {
            query: INITIAL_DATA_QUERY,
            variables: { id: 1 },
        },
        newData: jest.fn(() => ({
            data: {
                firstName: "Joe",
                lastName: "Random",
            }
        }))
    }];

    const mockSpy = mocks[0].newData;

    render(
        <MockedProvider mocks={mocks} addTypename={false}>
            <MyComponent/>
        </MockedProvider>
    )

    // Is it a loading view
    expect(result.asFragment()).toMatchSnapshot(); // Passes just fine, and matches expectations
    
    // Wait until the mock has been called once
    await waitFor(() => expect(mockSpy).toHaveBeenCalled(1)) // Also passes, meaning the mock was called

    // Has the page rendered once the loading mock has finished
    expect(result.asFragment()).toMatchSnapshot(); // Passes, but the page has rendered without any of the data
})

问题是这样的:当我运行这个测试时,所有三个测试都按预期通过,但在最终片段中,我渲染的组件中的数据丢失了。我确信正在调用模拟,因为我添加了一些记录器语句来检查。

真正令人困惑的部分是调用 mock 时的 loadingdataerror 值。我有一个useEffect 语句在它们中的任何一个发生变化时记录它们的值,当我运行测试时,输出如下所示:

{ loading: true, data: undefined, error: undefined }
{ loading: false, data: undefined, error: undefined }

这意味着正在调用钩子并开始加载,但是一旦加载结束,加载过程中发生的任何事情都不会返回任何数据,也不会产生任何错误。

有人知道我的问题可能是什么吗?我已经看了八种方式到星期天,但无法弄清楚。

【问题讨论】:

    标签: javascript reactjs mocking apollo react-testing-library


    【解决方案1】:

    我使用result 字段模拟了结果。

    result 字段可以是在执行任意逻辑后返回模拟响应的函数

    对我来说很好用。

    MyComponent.test.tsx:

    import { gql, useQuery } from '@apollo/client';
    import { useEffect } from 'react';
    
    export const INITIAL_DATA_QUERY = gql`
      query GetUser($id: ID!) {
        user(id: $id) {
          firstName
          lastName
        }
      }
    `;
    
    export default function MyComponent(props) {
      const result = useQuery(INITIAL_DATA_QUERY, { variables: { id: 1 } });
    
      useEffect(
        () =>
          console.log(
            JSON.stringify({
              loading: result.loading,
              data: result.data,
              error: result.error,
            }),
          ),
        [result],
      );
    
      if (result.loading) return <p>Fetching data...</p>;
      else if (result.error) return <p>{result.error}</p>;
      else return <p>{result.data.user.firstName}</p>;
    }
    

    MyComponent.test.tsx:

    import { render, waitFor } from '@testing-library/react';
    import MyComponent, { INITIAL_DATA_QUERY } from './MyComponent';
    import { MockedProvider } from '@apollo/client/testing';
    
    describe('68732957', () => {
      it('should load the data then render the page', async () => {
        const mocks = [
          {
            request: {
              query: INITIAL_DATA_QUERY,
              variables: { id: 1 },
            },
            result: jest.fn().mockReturnValue({
              data: {
                user: {
                  lastName: 'Random',
                  firstName: 'Joe',
                },
              },
            }),
          },
        ];
    
        const mockSpy = mocks[0].result;
        const result = render(
          <MockedProvider mocks={mocks} addTypename={false}>
            <MyComponent />
          </MockedProvider>,
        );
    
        expect(result.asFragment()).toMatchSnapshot();
        await waitFor(() => expect(mockSpy).toBeCalledTimes(1));
        expect(result.asFragment()).toMatchSnapshot();
      });
    });
    

    测试结果:

     PASS  src/stackoverflow/68732957/MyComponent.test.tsx
      68732957
        ✓ should load the data then render the page (58 ms)
    
      console.log
        {"loading":true}
    
          at src/stackoverflow/68732957/MyComponent.tsx:18:15
    
      console.log
        {"loading":false,"data":{"user":{"firstName":"Joe","lastName":"Random"}}}
    
          at src/stackoverflow/68732957/MyComponent.tsx:18:15
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   2 passed, 2 total
    Time:        0.736 s, estimated 1 s
    

    MyComponent.test.tsx.snap:

    // Jest Snapshot v1
    
    exports[`68732957 should load the data then render the page 1`] = `
    <DocumentFragment>
      <p>
        Fetching data...
      </p>
    </DocumentFragment>
    `;
    
    exports[`68732957 should load the data then render the page 2`] = `
    <DocumentFragment>
      <p>
        Joe
      </p>
    </DocumentFragment>
    `;
    

    软件包版本:

    "@testing-library/react": "^11.1.0",
    "react": "^17.0.1",
    "@apollo/client": "^3.4.7",
    "graphql": "^15.4.0"
    

    【讨论】:

      猜你喜欢
      • 2021-02-16
      • 1970-01-01
      • 2020-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-12
      • 2021-08-03
      • 2014-07-10
      相关资源
      最近更新 更多