阿波罗的 MockedProvider 没有明确警告丢失的模拟
Posted
技术标签:
【中文标题】阿波罗的 MockedProvider 没有明确警告丢失的模拟【英文标题】:apollo's MockedProvider doesn't warn clearly about missing mocks 【发布时间】:2020-05-22 19:12:04 【问题描述】:我正在使用 apollo 钩子(useQuery
、useMutation
)对 React 组件进行单元测试,并在测试中使用 apollo 的 MockedProvider
模拟实际查询。问题是有时,我的模拟与组件实际发出的查询不匹配(创建模拟时的拼写错误,或者组件演变并更改了一些查询变量)。发生这种情况时,MockedProvided
会向组件返回 NetworkError。但是在测试套件中,没有显示警告。这很令人沮丧,因为有时我的组件对useQuery
返回的错误无能为力。这导致我以前通过的测试突然无声无息地失败,让我很难找到原因。
这是一个使用useQuery
的组件示例:
import React from 'react';
import gql from 'apollo-boost';
import useQuery from '@apollo/react-hooks';
export const gqlArticle = gql`
query Article($id: ID)
article(id: $id)
title
content
`;
export function MyArticleComponent(props)
const data = useQuery(gqlArticle,
variables:
id: 5
);
if (data)
return (
<div className="article">
<h1>data.article.title</h1>
<p>data.article.content</p>
</div>
);
else
return null;
这是一个单元测试,我犯了一个错误,因为模拟的变量对象是id: 6
,而不是组件将请求的id: 5
。
it('the missing mock fails silently, which makes it hard to debug', async () =>
let gqlMocks = [
request:
query: gqlArticle,
variables:
/* Here, the component calls with "id": 5, so the mock won't work */
"id": 6,
,
result:
"data":
"article":
"title": "This is an article",
"content": "It talks about many things",
"__typename": "Article"
];
const container, findByText = render(
<MockedProvider mocks=gqlMocks>
<MyArticleComponent />
</MockedProvider>
);
/*
* The test will fail here, because the mock doesn't match the request made by MyArticleComponent, which
* in turns renders nothing. However, no explicit warning or error is displayed by default on the console,
* which makes it hard to debug
*/
let titleElement = await findByText("This is an article");
expect(titleElement).toBeDefined();
);
如何在控制台中显示明确的警告?
【问题讨论】:
我只是想感谢您提出这个问题并详细解释问题。我刚刚遇到了同样的问题,感谢上帝在这里找到了你的帖子!现在我的测试开始了! 【参考方案1】:我已向 apollo 团队提交了Github issue,以便建议一种内置方法来执行此操作。同时,这是我自制的解决方案。
这个想法是给MockedProvider
一个自定义的阿波罗链接。默认情况下,它使用MockLink
用给定的模拟初始化。取而代之的是,我创建了一个自定义链接,它是一个由MockLink
形成的链,我以与MockedProvider
相同的方式创建它,然后是一个阿波罗错误链接,它拦截可能由请求返回的错误,并将它们记录在控制台中。为此,我创建了一个自定义提供程序 MyMockedProvider
。
MyMockedProvider.js
import React from 'react'; import MockedProvider from '@apollo/react-testing'; import MockLink from '@apollo/react-testing'; import onError from "apollo-link-error"; import ApolloLink from 'apollo-link'; export function MyMockedProvider(props) let mocks, ...otherProps = props; let mockLink = new MockLink(mocks); let errorLoggingLink = onError(( graphQLErrors, networkError ) => if (graphQLErrors) graphQLErrors.map(( message, locations, path ) => console.log( `[GraphQL error]: Message: $message, Location: $locations, Path: $path`, ), ); if (networkError) console.log(`[Network error]: $networkError`); ); let link = ApolloLink.from([errorLoggingLink, mockLink]); return <MockedProvider ...otherProps link=link />;
MyArticleComponent.test.js
import React from 'react'; import render, cleanup from '@testing-library/react'; import MyMockedProvider from './MyMockedProvider'; import MyArticleComponent, gqlArticle from './MyArticleComponent'; afterEach(cleanup); it('logs MockedProvider warning about the missing mock to the console', async () => let gqlMocks = [ request: query: gqlArticle, variables: /* Here, the component calls with "id": 5, so the mock won't work */ "id": 6, , result: "data": "article": "title": "This is an article", "content": "It talks about many things", "__typename": "Article" ]; let consoleLogSpy = jest.spyOn(console, 'log'); const container, findByText = render( <MyMockedProvider mocks=gqlMocks> <MyArticleComponent /> </MyMockedProvider> ); let expectedConsoleLog = '[Network error]: Error: No more mocked responses for the query: query Article($id: ID) \n' + ' article(id: $id) \n' + ' title\n' + ' content\n' + ' __typename\n' + ' \n' + '\n' + ', variables: "id":5'; await findByText('"loading":false'); expect(consoleLogSpy.mock.calls[0][0]).toEqual(expectedConsoleLog); );
【讨论】:
并非所有英雄都穿斗篷 先生,您是救命稻草【参考方案2】:当 Apollo 的 MockedProvider
没有返回预期的响应时,可能很难调试出了什么问题。以下是我偶然发现的一些提示:
1。使用自定义MockedProvider
这可确保全局记录所有错误。如果您的测试预计会出现一些错误,可能会很吵。
2。记录或处理特定组件中的错误
如果不使用自定义 MockedProvider
,当您处理从查询或突变返回的 error
时,仍然可以提醒您缺少模拟:
const data, error = useQuery(myQuery);
console.log(error); // 'no more mocked responses'
3。在你的模拟中添加日志以查看它是否被调用
您的模拟返回一个 result
对象字面量。 result
也可以是一个函数,它只在你的模拟被调用时被执行。您可以从这里记录或添加其他工具来验证查询或突变是否已执行。
4。确保变量完全匹配
只有在调用查询或突变的变量与模拟完全匹配时才会调用模拟。
5。当一切都失败时
有时上述所有情况都很好,但您仍然无法从查询或突变中获取数据。这几乎可以肯定是因为您的模拟返回的数据与预期格式不完全匹配。 这将静默失败。除了验证您的数据格式之外,我还不知道如何解决此问题。
我什至遇到过这样的情况,即我的带有 Apollo 生成的类型的 TypeScript 代码正在编译,但我没有收到回复,因为我的一种类型略有偏差(也许我发送了额外的字段,我没有'不记得了)。
【讨论】:
以上是关于阿波罗的 MockedProvider 没有明确警告丢失的模拟的主要内容,如果未能解决你的问题,请参考以下文章
apollo MockedProvider + Storybook 不工作
使用 MockedProvider 进行测试时使用 Apollo 突变更新存储
使用酶进行测试时如何找到包裹在 MockedProvider 中的 Typography 组件