Apollo MockedProvider 始终返回数据字段未定义,即使在加载后也是如此

Ian*_*ane 10 javascript mocking apollo reactjs react-testing-library

一些背景:我有一个组件在加载时立即调用 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
}
Run Code Online (Sandbox Code Playgroud)

当我在野外运行这个组件时,一切都完全按预期工作。它通过 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
})
Run Code Online (Sandbox Code Playgroud)

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

真正令人困惑的部分是调用模拟时的loadingdata和值。error当它们中的任何一个发生更改时,我有一个useEffect语句记录它们的值,当我运行测试时,输出如下所示:

{ loading: true, data: undefined, error: undefined }
{ loading: false, data: undefined, error: undefined }
Run Code Online (Sandbox Code Playgroud)

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

有谁知道我这里的问题可能是什么?到周日为止我已经看了八种方法,但还是无法弄清楚。

sli*_*wp2 3

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

\n
\n

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

\n
\n

这对我来说可以。

\n

MyComponent.test.tsx

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

MyComponent.test.tsx

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

测试结果:

\n
 PASS  src/stackoverflow/68732957/MyComponent.test.tsx\n  68732957\n    \xe2\x9c\x93 should load the data then render the page (58 ms)\n\n  console.log\n    {"loading":true}\n\n      at src/stackoverflow/68732957/MyComponent.tsx:18:15\n\n  console.log\n    {"loading":false,"data":{"user":{"firstName":"Joe","lastName":"Random"}}}\n\n      at src/stackoverflow/68732957/MyComponent.tsx:18:15\n\nTest Suites: 1 passed, 1 total\nTests:       1 passed, 1 total\nSnapshots:   2 passed, 2 total\nTime:        0.736 s, estimated 1 s\n
Run Code Online (Sandbox Code Playgroud)\n

MyComponent.test.tsx.snap

\n
// Jest Snapshot v1\n\nexports[`68732957 should load the data then render the page 1`] = `\n<DocumentFragment>\n  <p>\n    Fetching data...\n  </p>\n</DocumentFragment>\n`;\n\nexports[`68732957 should load the data then render the page 2`] = `\n<DocumentFragment>\n  <p>\n    Joe\n  </p>\n</DocumentFragment>\n`;\n
Run Code Online (Sandbox Code Playgroud)\n

软件包版本:

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