gpt4 book ai didi

reactjs - 如何对 apollo 链接进行单元测试

转载 作者:行者123 更新时间:2023-12-02 18:52:31 27 4
gpt4 key购买 nike

我目前有点困惑如何在我的 React 应用程序中测试一些 apollo 链接,因为官方文档似乎只提供有关如何在组件连接到提供程序后测试组件的建议。

目前我有 2 个链接:一个用于注入(inject)授权 token ,另一个用于在服务器返回响应后刷新它;我想要做的是要么单独测试它们,要么测试客户端(将在这些链接和一个简单的 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的useQueryuseMutation钩子(Hook)之一通过带有模拟的客户端来触发“假”请求响应,但似乎这个模拟提供者实际上在解析虚假数据之前模拟了客户端,所以它不适合我。

我考虑的第二个选项是 this guide ,它基本上将您的链接与您调用 expect 方法的自定义“断言”链接连接起来。

虽然很有希望,但该实现并不适合我开箱即用,因为测试没有等待 execute 调用完成(没有断言设法执行),所以我做了一些更改将其包装在一个 Promise 中,如下所示:

// 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);
});
});

这适用于第一个链接,我只是注入(inject)一个 header ,但在第二个链接上,断言总是失败,仔细观察后,似乎我在 map 中定义的内容方法从未被调用。

现在,我不确定这是否是进行此类测试的正确方法,因为有关该主题的文档有点缺乏。我想知道的是:

  • 这种测试 apollo 的方法实际上可行吗?或者有更好的方法来测试我的客户端配置吗?
  • 如果我继续使用这种方法,有没有办法可以强制在链接上调用 map 方法?

任何帮助将不胜感激。

最佳答案

我有同样的一般性问题,并决定在链接执行工具中的适当时间解决 promise 的相同答案。

您没有看到map接到电话有几个原因:

  • map在可观察的结果上调用,并且您没有从终止链接返回结果,因此 map永远不会在您的测试链接上被调用。
  • 如果您确实在终止链接中返回了结果,则当前执行断言的位置是之前 map可以调用。您需要将断言延迟到响应处理逻辑运行之后。

可以简化您的工具,只添加一个终止链接,然后将您的 promise 解析逻辑移至订阅调用中。请参阅下面的示例:

import { ApolloLink, execute, FetchResult, gql, GraphQLRequest, Observable, Operation } from '@apollo/client';

const MockQuery = gql`
query {
thing
}
`;

interface LinkResult<T> {
operation: Operation;
result: FetchResult<T>;
}

async function executeLink<T = any, U = any>(
linkToTest: ApolloLink,
request: GraphQLRequest = { query: MockQuery },
responseToReturn: FetchResult<U> = { data: null }
) {
const linkResult = {} as LinkResult<T>;

return new Promise<LinkResult<T>>((resolve, reject) => {
const terminatingLink = new ApolloLink((operation) => {
linkResult.operation = operation;
return Observable.of(responseToReturn);
});

execute(ApolloLink.from([linkToTest, terminatingLink]), request).subscribe(
(result) => {
linkResult.result = result as FetchResult<T>;
},
(error) => {
reject(error);
},
() => {
resolve(linkResult);
}
);
});
}

it('calls refreshToken', async () => {
const refreshToken = jest.fn();
const resetTokenLink = new ApolloLink((operation, forward) => {
operation.variables.test = 'hi';

return forward(operation)
.map((response) => {
refreshToken(operation.getContext());
return response;
})
.map((response) => {
(response.context ??= {}).addedSomething = true;
return response;
});
});

const { operation, result } = await executeLink(resetTokenLink);

expect(refreshToken).toHaveBeenCalled();
expect(operation.variables.test).toBe('hi');
expect(result.context?.addedSomething).toBe(true);
});

我只是捕获 promise 中的操作和结果值,而不是插入断言逻辑以在链接内运行。您当然可以创建其他自定义链接以在链接链中的特定点插入断言,但在最后断言结果似乎更好。

关于reactjs - 如何对 apollo 链接进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66573227/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com