reactjs - 阿波罗的 MockedProvider 没有明确警告丢失的模拟
问题描述
我正在使用 apollo hooks ( 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();
});
如何在控制台中显示明确的警告?
解决方案
我已经向 apollo 团队提交了一个Github 问题,以建议一种内置的方法来做到这一点。同时,这是我自制的解决方案。
这个想法是给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); });
推荐阅读
- java - 用于在 char 'x' 之前选择 upto char before char 'y' 的正则表达式
- google-fusion-tables - 提取GEE中多个点的波段值
- javascript - 发生视频广告跳过按钮事件时,焦点无法转移到 iframe 游戏
- excel - 将excel打印页面转换为pdf并在打印页面上发送到电子邮件
- c++ - 实现nd-array时如何处理C++中的“超出范围”异常
- angular - Angular:如何更改输入类型 =“日期”占位符值
- matlab - 如何将 Gabor 小波应用于 3d 体积?
- javascript - Object.assign() 与 angular.extend()
- excel - 需要帮助以减少以下代码的执行时间
- joomla - 如何在 JSE 中按名称而不是 id 加载模块?