使用 Hooks 在 React 中对 Apollo Graphql 进行单元测试
Posted
技术标签:
【中文标题】使用 Hooks 在 React 中对 Apollo Graphql 进行单元测试【英文标题】:Unit testing Apollo Graphql in React using Hooks 【发布时间】:2019-12-19 23:54:38 【问题描述】:我正在尝试使用 React 和 Apollo Graphql 创建一个单元测试,但是我不断收到此错误:
Watch Usage: Press w to show more. console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:104
Warning: An update to ThemeHandler inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() =>
/* fire events that update state */
);
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
in ThemeHandler (at theme-handler.spec.tsx:51)
in ApolloProvider (created by MockedProvider)
in MockedProvider (at theme-handler.spec.tsx:50)
这是我的代码:
import createMuiTheme, MuiThemeProvider from '@material-ui/core';
import * as Sentry from '@sentry/browser';
import React, useState from 'react';
import BrandTheme, useGetBrandThemeQuery from '../../generated/graphql';
/**
* Handles the app theme. Will set the default theme or the brand theme taken from the backend.
*/
export default function ThemeHandler(props: React.PropsWithChildren<any>): React.ReactElement
const brandId = Number(process.env.REACT_APP_BRAND);
// Default Onyo theme
const [theme, setTheme] = useState(
palette:
primary: main: '#f65a02' ,
secondary: main: '#520075' ,
,
typography:
fontFamily: 'Quicksand, sans-serif',
,
);
useGetBrandThemeQuery(
variables: brandId ,
skip: brandId <= 0,
onCompleted: data =>
if (
!data.brandTheme ||
!data.brandTheme.brandThemeColor ||
data.brandTheme.brandThemeColor.length === 0
)
console.warn('Empty brand theme returned, using default');
Sentry.captureMessage(`Empty brand theme for brandId: $brandId`, Sentry.Severity.Warning);
else
const palette = parseBrandPalette(data.brandTheme as BrandTheme);
setTheme( ...theme, palette );
console.log('Theme', theme, data.brandTheme);
,
);
return <MuiThemeProvider theme=createMuiTheme(theme)>props.children</MuiThemeProvider>;
function parseBrandPalette(brandTheme: BrandTheme)
const pallete: any = ;
for (const color of brandTheme.brandThemeColor!)
if (color && color.key === 'primaryColor')
pallete.primary = main: color.value ;
else if (color && color.key === 'darkPrimaryColor')
pallete.secondary = main: color.value ;
return pallete;
我的测试:
import renderer from 'react-test-renderer';
import React from 'react';
import ThemeHandler from './theme-handler';
import MockedProvider, wait from '@apollo/react-testing';
import GetBrandThemeDocument from '../../generated/graphql';
import Button from '@material-ui/core';
const act = renderer;
describe('Theme Handler', () =>
const originalEnv = process.env;
beforeEach(() =>
// https://***.com/questions/48033841/test-process-env-with-jest/48042799
jest.resetModules();
process.env = ...originalEnv ;
delete process.env.REACT_APP_BRAND;
);
afterEach(() =>
process.env = originalEnv;
);
it('should use a theme retrieved from the backend', async () =>
process.env.REACT_APP_BRAND = '39';
const mocks = [
request:
query: GetBrandThemeDocument,
variables: brandId: 39 ,
,
result:
data:
brandTheme:
brandThemeColor: [
key: 'primaryColor', value: '#182335' ,
key: 'darkPrimaryColor', value: '#161F2F' ,
],
,
,
,
,
];
let wrapper;
act(() =>
wrapper = renderer.create(
<MockedProvider mocks=mocks addTypename=false>
<ThemeHandler>
<Button color='primary' id='test-obj'>
Hello world!
</Button>
</ThemeHandler>
</MockedProvider>
);
);
await wait(0);
expect(wrapper).toBeTruthy();
);
);
我也试过用 Enzyme 的 mount
代替 React 测试渲染器,但结果是一样的。
据我所知,这个错误是因为我正在使用异步函数和钩子更改当前状态而引起的。但我不确定我可以做些什么不同的事情来实现这一点。
【问题讨论】:
【参考方案1】:我通过使用act
包装测试中的所有内容解决了我的问题。我认为发生此错误是因为部分测试包含在 act
中,但异步部分未包含,因此更改发生在此函数范围之外。
这是更新的测试,即通过:
import React from 'react';
import ThemeHandler from './theme-handler';
import MockedProvider, wait from '@apollo/react-testing';
import GetBrandThemeDocument from '../../generated/graphql';
import Button from '@material-ui/core';
import mount from 'enzyme';
import act from 'react-dom/test-utils';
describe('Theme Handler', () =>
const originalEnv = process.env;
beforeEach(() =>
// https://***.com/questions/48033841/test-process-env-with-jest/48042799
jest.resetModules();
process.env = ...originalEnv ;
delete process.env.REACT_APP_BRAND;
);
afterEach(() =>
process.env = originalEnv;
);
it('should use a theme retrieved from the backend', async () =>
process.env.REACT_APP_BRAND = '39';
await act(async () =>
const mocks = [
request:
query: GetBrandThemeDocument,
variables: brandId: 39 ,
,
result:
data:
brandTheme:
brandThemeColor: [
key: 'primaryColor', value: '#182335' ,
key: 'darkPrimaryColor', value: '#161F2F' ,
],
,
,
,
,
];
const wrapper = mount(
<MockedProvider mocks=mocks addTypename=false>
<ThemeHandler>
<Button color='primary' id='test-obj'>
Hello world!
</Button>
</ThemeHandler>
</MockedProvider>
);
expect(wrapper).toBeTruthy();
await wait(0);
wrapper.update();
expect(wrapper.find('#test-obj')).toBeTruthy();
);
);
);
【讨论】:
以上是关于使用 Hooks 在 React 中对 Apollo Graphql 进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章