【发布时间】:2021-06-08 21:31:06
【问题描述】:
我目前有点卡在如何在我的 react 应用程序中测试一些 apollo 链接,因为官方文档似乎只提供了有关如何在组件连接到提供程序后对其进行测试的建议。
目前我有 2 个链接:一个用于注入授权令牌,一个用于在服务器返回响应后刷新它;我想要做的是单独测试它们,或者测试客户端(将在这些链接上构建,以及一个简单的HttpLink)在满足条件时执行它们的逻辑。
以下是它们的实现:
// InjectToken.ts
import { setContext } from '@apollo/client/link/context';
import { getToken } from '../../authentication';
const authenticationLink = setContext(async (_, { headers }) => {
// Fetches the token from the local storage
const token = await getToken();
if (token) {
return {
headers: {
...headers,
Authorization: `Bearer ${token}`
}
}
}
return { headers };
});
export default authenticationLink;
// RefreshToken.ts
import { ApolloLink } from '@apollo/client';
import { refreshToken } from '../../authentication';
const resetTokenLink = new ApolloLink(
(operation, forward) => forward(operation).map(response => {
const context = operation.getContext();
refreshToken(context);
return response;
})
);
export default resetTokenLink;
我虽然关于使用 MockProvider 和 apollo 的 useQuery 或 useMutation 钩子之一通过模拟响应通过客户端触发“假”请求,但似乎这个模拟提供者实际上在解析之前模拟了客户端假数据,所以它不在我的桌子上。
我考虑的第二个选项是关注this guide,它基本上将您的链接与您调用expect 方法的自定义“断言”链接连接起来。
虽然很有希望,但该实现对我来说并不能开箱即用,因为测试没有等待 execute 调用完成(没有断言成功执行),所以我做了一些更改以将其包装在一个承诺中像这样:
// mockAssertForLink.ts
// This performs the mock request
async function mockExecuteRequest(link: ApolloLink): Promise<void> {
return new Promise<void>((resolve): void => {
const lastLink = new ApolloLink(() => {
resolve();
return null;
})
execute(ApolloLink.from([link, lastLink]), { query: MockQuery}).subscribe((): void => {
// Not required for our tests, subscribe merely fires the request
});
})
}
// This exposes the assertionCallback after the promise fulfills, and reports the operation object.
export default async function mockAssertForLink(
link: ApolloLink,
assertionCallback: (operation: Operation) => void
): Promise<void> {
return mockExecuteRequest(ApolloLink.from([
link,
new ApolloLink((operation, forward) => {
assertionCallback(operation);
return forward(operation);
})
]))
}
通过这个实现,我基本上为每个要对其执行测试的链接创建了两个额外的链接:
- 一个公开断言回调,我可以在其中检查操作的上下文
- 一个实际调用
Promise.resolve()的函数,这将防止我的异步测试在执行时卡住
我的测试是这样使用mockAssertForLink:
// InjectToken.test.ts
it('correctly injects authorization header', async () => {
mocked(getToken).mockResolvedValue(mockToken);
await mockAssertForLink(authenticationLink, operation => {
expect(operation.getContext().headers.Authorization).toBe(`Bearer ${mockToken}`)
});
});
// RefreshToken.ts
it('correctly refreshes the token', async () => {
await mockAssertForLink(resetTokenLink, () => {
expect(refreshToken).toHaveBeenCalledTimes(1);
});
});
这适用于第一个链接,我只是在其中注入一个标头,但在第二个链接上,断言总是失败,仔细观察后,似乎我在 map 方法中定义的内容从未被调用过。
现在,我不确定这是否是进行此类测试的正确方法,因为有关该主题的文档有点缺乏。我想知道的是:
- 这种测试 apollo 的方法实际上是否可行,还是有更好的方法来测试我的客户的配置?
- 如果我继续使用这种方法,有没有办法可以强制在链接上调用
map方法?
任何帮助将不胜感激。
【问题讨论】:
标签: reactjs typescript unit-testing mocking apollo-client