使用预加载查询测试 Relay
使用预加载查询的组件(useQueryLoader
和 usePreloadedQuery
钩子)需要略微不同且更复杂的测试设置。
简而言之,在渲染组件之前需要执行两个步骤。
- 配置查询解析器以通过
environment.mock.queueOperationResolver
生成响应。 - 通过
environment.mock.queuePendingOperation
记录挂起的队列调用。
出现问题的症状
- 测试没有达到预期结果。
- 查询似乎处于阻塞状态,而不是执行。
- 例如,
Suspend
不会从“等待”状态切换到“数据已加载”状态。
- 例如,
- 如果在
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
一起使用。
此页面有用吗?
通过以下方式帮助我们改进网站: 回答几个简短的问题.