首页 > 解决方案 > 如何测试包含子组件并使用上下文的样式化组件?

问题描述

我有一个名为的父组件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">

但我不知道如何在测试中通过那门课?

标签: reactjsjestjsenzymereact-testing-library

解决方案


测试失败的原因是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);
   });
});

Codesandbox 分叉


推荐阅读