当一个组件有多个 useQueries 时,Apollo MockedProvider 不起作用?

Apollo's MockedProvider doenst work when a component has multiple useQuery's?

我有一个简单的 Apollo 设置。在这个 CodeSandbox 示例中,我使用的是 MockedProvider,但实时的也可以:

https://codesandbox.io/s/winter-glitter-o8ug5?file=/src/App.js:0-1164

import { ApolloClient, InMemoryCache } from "@apollo/client";
import { gql, useQuery, ApolloProvider } from "@apollo/client";
import { MockedProvider } from "@apollo/client/testing";

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache()
});

const EXCHANGE_RATES_1 = gql`
  query GetExchangeRates {
    rates(currency: "USD") {
      rate
    }
  }
`;

const mocks = [
  {
    request: {
      query: EXCHANGE_RATES_1
    },
    result: {
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            rate: "123"
          }
        ]
      }
    }
  }
];

const MyComponent = () => {
  const { data: data1, loading: loading1 } = useQuery(EXCHANGE_RATES_1);
  if (loading1) return <p>Loading...</p>;
  return (
    <div>
      <h1>{data1?.rates[0].rate}</h1>
    </div>
  );
};

const AppMocked = () => {
  return (
    <MockedProvider addTypename={false} mocks={mocks}>
      <MyComponent />
    </MockedProvider>
  );
};

const AppLive = () => {
  return (
    <ApolloProvider client={client}>
      <MyComponent />
    </ApolloProvider>
  );
};

export default AppMocked;

我需要在 MyComponent 中添加一个 useQuery。我知道这是一个奇怪的例子,因为它应该只是一个查询,但它说明了我 运行 遇到的问题。

https://codesandbox.io/s/magical-dewdney-j451d?file=/src/App.js:0-1630

import { ApolloClient, InMemoryCache } from "@apollo/client";
import { gql, useQuery, ApolloProvider } from "@apollo/client";
import { MockedProvider } from "@apollo/client/testing";

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache()
});

const EXCHANGE_RATES_1 = gql`
  query GetExchangeRates {
    rates(currency: "USD") {
      rate
    }
  }
`;
const EXCHANGE_RATES_2 = gql`
  query GetExchangeRates {
    rates(currency: "USD") {
      currency
    }
  }
`;

const mocks = [
  {
    request: {
      query: EXCHANGE_RATES_1
    },
    result: {
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            rate: "123"
          }
        ]
      }
    }
  },
  {
    request: {
      query: EXCHANGE_RATES_2
    },
    result: {
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            currency: "YOLO"
          }
        ]
      }
    }
  }
];

const MyComponent = () => {
  const { data: data1, loading: loading1 } = useQuery(EXCHANGE_RATES_1);
  const { data: data2, loading: loading2 } = useQuery(EXCHANGE_RATES_2);
  if (loading1 || loading2) return <p>Loading...</p>;
  return (
    <div>
      <h1>{data1?.rates[0].rate}</h1>
      <h2>{data2?.rates[0].currency}</h2>
    </div>
  );
};

const AppMocked = () => {
  return (
    <MockedProvider addTypename={false} mocks={mocks}>
      <MyComponent />
    </MockedProvider>
  );
};

const AppLive = () => {
  return (
    <ApolloProvider client={client}>
      <MyComponent />
    </ApolloProvider>
  );
};

export default AppMocked;

现场版工作正常,但模拟版的 H1 为空。当我注销 data1 时,我可以看到它最初确实有数据,但在随后的渲染中变为 'undefined'

我在文档中看不到任何内容来解释为什么模拟不起作用:https://www.apollographql.com/docs/react/development-testing/testing/

出于某种原因,在您的设置中,查询被调用了不止一次。

这会导致在后台出现“查询不再有模拟响应”错误(请参阅 mockLink.ts),因为每个查询调用都会消耗一个模拟响应。
第一次调用查询时,它 returns 模拟数据,这就是您最初看到它的原因。下一次调用会抛出错误并将数据设置为 undefined.

我找到了两种解决方法:

选项 1 通过将 fetch-policy 设置为 no-cache:

禁用 MockedProvider 上的缓存
<MockedProvider 
  mocks={mocks} 
  defaultOptions={{
    watchQuery: { fetchPolicy: 'no-cache' },
    query: { fetchPolicy: 'no-cache' },
  }}
>
  <MyComponent />
</MockedProvider>

选项 2 通过提供 newData 函数允许多次使用模拟响应:

const mocks = [
  {
    request: {
      query: EXCHANGE_RATES_1
    },
    result: {
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            rate: "123"
          }
        ]
      }
    },
    newData: () => ({
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            rate: "123"
          }
        ]
      }
    })
  },
  {
    request: {
      query: EXCHANGE_RATES_2
    },
    result: {
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            currency: "YOLO"
          }
        ]
      }
    },
    newData: () => ({
      data: {
        rates: [
          {
            __typename: "ExchangeRate",
            currency: "YOLO"
          }
        ]
      }
    })
  }
];