跳至主要内容
版本: v18.0.0

使用预加载查询测试 Relay

使用预加载查询的组件(useQueryLoaderusePreloadedQuery 钩子)需要略微不同且更复杂的测试设置。

简而言之,在渲染组件之前需要执行两个步骤。

  1. 配置查询解析器以通过 environment.mock.queueOperationResolver 生成响应。
  2. 通过 environment.mock.queuePendingOperation 记录挂起的队列调用。

出现问题的症状

  1. 测试没有达到预期结果。
  2. 查询似乎处于阻塞状态,而不是执行。
    1. 例如,Suspend 不会从“等待”状态切换到“数据已加载”状态。
  3. 如果在 usePreloadedQuery 前后添加 console.log,则只命中“之前”调用。

简而言之

const {RelayEnvironmentProvider} = require('react-relay');
const {MockPayloadGenerator, createMockEnvironment} = require('relay-test-utils');
const {act, render} = require('@testing-library/react');
test("...", () => {
// arrange
const environment = createMockEnvironment();
environment.mock.queueOperationResolver(operation => {
return MockPayloadGenerator.generate(operation, {
CurrencyAmount() {
return {
formatted_amount: "1234$",
};
},
});
});
const query = YourComponentGraphQLQueryGoesHere; // can be the same, or just identical
const variables = {
// ACTUAL variables for the invocation goes here
};
environment.mock.queuePendingOperation(YourComponentGraphQLQuery, variables);

// act
const {getByTestId, ..otherStuffYouMightNeed} = render(
<RelayEnvironmentProvider environment={environment}>
<YourComponent data-testid="1234" {...componentPropsIfAny}/>
</RelayEnvironmentProvider>
);
// trigger the loading - click a button, emit an event, etc. or ...
act(() => jest.runAllImmediates()); // ... if loadQuery is in the useEffect()
// assert
// your assertions go here
});

配置查询解析器以生成响应

这是通过 environment.mock.queueOperationResolver(operation) 调用来完成的,但要使其正常工作可能很棘手。

此调用的关键是返回以非常特定的格式(确切地说,是 MockResolvers 类型)模拟的 graphql 结果。这是通过 generate 的第二个参数来完成的 - 它是一个对象,其键是我们想要模拟的 GraphQL 类型。(参见 mock-payload-generator)。

继续上面的例子。

return MockPayloadGenerator.generate(operation, {
CurrencyAmount() { // <-- the GraphQL type
return {
formatted_amount: "response_value" <-- CurrencyAmount fields, selected in the query
};
}
});

这里棘手的地方是要获取要返回的 GraphQL 类型和字段的名称。这可以通过两种方法完成。

  • 调用 console.log(JSON.stringify(operation, null, 2)) 并查找与我们要模拟的内容相对应的 concreteType。然后查看兄弟 selections 数组,它描述了从该对象中选择的字段。

可以通过 模拟解析器上下文 为不同的查询变量返回不同的数据。查询变量将在 context.args 上可用,但仅对最内层的函数调用可用(对于上面的查询,只有 offer_ids 可用)。

CurrencyAmount(context) {
console.log(JSON.stringify(context, null, 2)); // <--
return { formatted_amount: mockResponse }
}
// <-- logs { ...snip..., "name": "subtotal_price_for_offers", args: { offer_ids: [...] } }

记录挂起的队列调用

这更直接 - 它是通过调用 environment.mock.queuePendingOperation(query, variables) 来完成的。

  • Query 必须与组件发出的查询匹配。最简单(也是最能防止查询更改)的方法是从组件模块导出查询并在测试中使用它,但拥有一个相同(但不是同一个)查询也能正常工作。
  • variables 必须与将在本次测试调用中使用的变量匹配。
    • 注意嵌套的对象和数组 - 它们是通过 areEqual (调用代码) 进行比较的。
      • 数组是通过值(而不是通过引用)进行比较的,但元素的顺序很重要。
      • 嵌套对象 - 执行深度比较,键的顺序无关紧要(这尚未得到确认 - 如果您使用具有“深度”结构的 graphql 查询,请更新此文档)。

故障排除

  • console.log,无处不在的 console.log!推荐位置。
    • 组件:useQueryLoader, usePreloadedQuery, loadQuery 之前和之后。
    • 测试:在 queueOperationResolver 回调中。
    • 库:在 RelayModernMockEnvironment.execute 中,const currentOperation = ... 调用之后 (这里)。
  • 如果 loadQuery 未被调用 - 确保发出触发事件。根据您的组件实现,它可以是用户操作(例如按钮点击或按键)、javascript 事件(通过事件发射器机制)或带有 useEffect 的简单“延迟执行”。
    • useEffect 案例可能是最容易被忽略的 - 确保在渲染组件之后调用 act(() => jest.runAllImmediates())
  • 如果 usePreloadedQuery “之前”命中,但“之后”未命中 - 查询被挂起。本指南是为了解决此问题而编写的 - 您可能需要重新阅读它。但最有可能的原因是以下之一。
    • 使用不同的查询 - 查询解析器不会被调用,currentOperation 将为 null
    • 查询变量不匹配 - 查询解析器不会被调用,currentOperation 将为 null(确保检查 variables)。
      • 此外,确保数组(如果有)按相同的顺序排列(或者更好,使用集合,如果可能的话)。
  • 如果从查询返回的数据不是您预期的,请确保您正在生成正确的 graphql 类型。
    • 如果返回值类似于 <mock-value-for-field-"formatted_amount">,则可以判断您正在模拟错误的类型。
注意

确保组件和测试使用相同的环境(即在您的测试 React 树中没有任何地方存在 <RelayEnvironmentProvider environment={RelayFBEnvironment}>)。

结语

这里的示例使用 testing-library-react,但它也能与 react-test-renderer 一起使用。


此页面有用吗?

通过以下方式帮助我们改进网站: 回答几个简短的问题.