reactjs - 如何测试包含子组件并使用上下文的样式化组件?
问题描述
我有一个名为的父组件DismissButton
:
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { StyledDismissButton } from './Styled';
import { IoClose } from 'react-icons/io5';
import VisibilityContext from '../../context';
export const DismissButton = () => {
const { setVisible } = useContext(VisibilityContext);
const hideToast = () => setVisible(false);
return (
<StyledDismissButton aria-label="Close" onClick={hideToast}>
<IoClose />
</StyledDismissButton>
);
};
StyledDismissButton.propTypes = {
onClick: PropTypes.func.isRequired,
};
export { StyledDismissButton };
它消耗这个context
:
import { createContext } from 'react';
const initialState = {
visible: true,
setVisible: () => {},
};
const VisibilityContext = createContext(initialState);
export default VisibilityContext;
它包含一个子组件StyledDissmissButton
:
import styled from "styled-components";
const StyledDismissButton = styled.span`
right: 0;
cursor: pointer;
`;
export { StyledDismissButton };
我想DismissButton
用 Enzyme 和 Jest 进行测试,但每次都失败了,因为组件消耗了一个上下文。我已经尝试过这个解决方案,但它对我不起作用。
测试
import 'jsdom-global/register';
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import DismissButton, { StyledDismissButton } from './index';
import { IoClose } from 'react-icons/io';
Enzyme.configure({ adapter: new Adapter() });
it('renders one <StyledDissmisButton> when passed in', () => {
const contextValue = {
visible: true,
setVisible: () => {},
hideToast() {
this.setVisible(false);
},
};
const element = shallow(<DismissButton />);
expect(
element.contains(
<StyledDismissButton aria-label="Close" onClick={contextValue.hideToast}>
<IoClose />
</StyledDismissButton>
)
).toEqual(true);
});
测试结果
expect(received).toEqual(expected)
Expected value to equal:
true
Received:
false
at Object.<anonymous> (src/components/Toast/Header/DismissButton/index.test.js:32:7)
at new Promise (<anonymous>)
at node_modules/p-map/index.js:46:16
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 4 passed, 5 total
Snapshots: 1 passed, 1 total
Time: 13.773s
你可以查看React Toast上的 repos
调试结果
<TestComponent>
<Component>
<styled.span aria-label="Close" onClick={[Function: hideToast]}>
<span aria-label="Close" onClick={[Function: hideToast]} className="sc-bdnxRM gsGVlo">
<IoClose>
<IconBase attr={{...}}>
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 512 512" className={[undefined]} style={{...}} height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<path d="M289.94 256l95-95A24 24 0 00351 127l-95 95-95-95a24 24 0 00-34 34l95 95-95 95a24 24 0 1034 34l95-95 95 95a24 24 0 0034-34z" />
</svg>
</IconBase>
</IoClose>
</span>
</styled.span>
</Component>
</TestComponent>
更新
经过深入关注后,我发现 Styled Component 生成向组件添加一个类,这使得匹配变得不可能:
<span aria-label="Close" onClick={[Function: hideToast]} className="sc-bdnxRM gsGVlo">
但我不知道如何在测试中通过那门课?
解决方案
测试失败的原因是hideToast
在组件范围内创建的和hideToast
在测试中定义的函数在引用上是不同的。当wrapper.contains
检查 上的onClick
道具时StyledDismissButton
,它失败了,因为这两个功能不一样。
解决这个问题的一种方法是在上下文本身中实现并按照钩子hideToast
提供的方式使用它。useContext
const initialState = {
visible: true,
setVisible: () => {},
hideToast(){
this.setVisible(false)
}
};
export const DismissButton = () => {
const { hideToast } = useContext(VisibilityContext);
return (
<StyledDismissButton aria-label="Close" onClick={hideToast}>
<IoClose />
</StyledDismissButton>
);
};
道具现在匹配,测试通过。
describe('<DismissButton/>', () => {
it('renders one <StyledDissmisButton> when passed in', () => {
const contextValue = {
visible: true,
setVisible: () => {},
hideToast(){
this.setVisible(false)
}
};
const TestComponent = () => {
return (
<Context.Provider value={contextValue}>
<DismissButton />
</Context.Provider>
);
};
const element = mount(<TestComponent />);
expect(
element.contains(
<StyledDismissButton aria-label="Close" onClick={contextValue.hideToast}>
<IoClose />
</StyledDismissButton>
)
).toEqual(true);
});
});