【问题标题】:React Testing: Event handlers in React Shallow Rendering unit testsReact 测试:React 浅层渲染单元测试中的事件处理程序
【发布时间】:2015-12-01 04:11:31
【问题描述】:

背景

我正在尝试学习如何使用 React Shallow Rendering TestUtil 并让测试通过,直到我向两者添加了 onClick 事件处理程序;看来我在Accordion.test.jsthis.toggle 中尝试使用的Accordion.toggle 函数与Accordian.js 中的this.toggle 之间肯定存在一些差异......但我无法弄清楚。

问题

如何让Accordian.test.js 中突出显示的两个测试通过?

重现步骤

  1. 克隆https://github.com/trevordmiller/shallow-rendering-testing-playground
  2. npm install
  3. npm run dev - 当您点击“Lorem Ipsum”时看到该组件正在工作
  4. npm run test:watch - 看到测试失败了

【问题讨论】:

    标签: javascript unit-testing testing reactjs mocha.js


    【解决方案1】:

    有许多问题阻止您的测试通过。

    查看测试“默认情况下应该是不活动的”:

    1. 您的测试中的Accordion.toggleAccordion 类的属性,而您的代码中的this.toggleAccordion 类的一个实例的属性 - 所以在这种情况下,您要比较两个不同的东西。要访问测试中的“实例”方法,您可以将Accordion.toggle 替换为Accordion.prototype.toggle。如果不是 this.toggle = this.toggle.bind(this); 在您的构造函数中,这将起作用。这就引出了第二点。

    2. 当您在函数上调用 .bind() 时,它会在运行时创建一个新函数 - 因此您无法将其与原始 Accordion.prototype.toggle 进行比较。解决此问题的唯一方法是将“绑定”函数从渲染结果中拉出:

      let toggle = result.props.children[0].props.onClick;
      
      assert.deepEqual(result.props.children, [
        <a onClick={toggle}>This is a summary</a>,
        <p style={{display: 'none'}}>This is some details</p>
      ]);
      

    至于你的第二个失败的测试“应该在点击时激活”:

    1. 您尝试调用不存在的result.props.onClick()。你是想打电话给result.props.children[0].props.onClick();

    2. React 中存在一个错误,需要在使用浅渲染调用 setState 时声明全局“文档”变量 - 如何在每种情况下解决此问题超出了此问题的范围,但可以快速解决您通过的测试是在调用 onClick 方法之前添加global.document = {};。换句话说,您的原始测试有:

      result.props.onClick();
      

      现在应该说:

      global.document = {};
      result.props.children[0].props.onClick();
      

      请参阅“修复损坏的 setState()”部分on this pagethis react issue

    【讨论】:

      【解决方案2】:

      给定一个带有 onClick 属性的嵌套元素和一个上下文绑定到组件的处理程序:

      render() {
        return (
          <div>
            <a className="link" href="#" onClick={this.handleClick}>
              {this.state.linkText}
            </a>
            <div>extra child to make props.children an array</div>
          </div>
        );
      }
      
      handleClick(e) {
        e.preventDefault();
        this.setState({ linkText: 'clicked' });
      }
      

      您可以手动调用 onClick 属性的函数值,在事件对象中存根:

      it('updates link text on click', () => {
        let tree, link, linkText;
      
        const renderer = TestUtils.createRenderer();
        renderer.render(<MyComponent />);
      
        tree = renderer.getRenderOutput();
        link = tree.props.children[0];
        linkText = link.props.children;
      
        // initial state set in constructor
        expect(linkText).to.equal('Click Me');
      
        // manually invoke onClick handler via props
        link.props.onClick({ preventDefault: () => {} });
      
        tree = renderer.getRenderOutput();
        link = tree.props.children[0];
        linkText = link.props.children;
      
        expect(linkText).to.equal('Clicked');
      });
      

      【讨论】:

        【解决方案3】:

        要测试onClick 之类的用户事件,您必须使用TestUtils.Simulate.clickSadly:

        目前无法将 ReactTestUtils.Simulate 与浅渲染一起使用,我认为接下来的问题应该是:https://github.com/facebook/react/issues/1445

        【讨论】:

          【解决方案4】:

          我已经成功测试了我在无状态组件中的点击。方法如下:

          我的组件:

          import './ButtonIcon.scss';
          
          import React from 'react';
          import classnames from 'classnames';
          
          const ButtonIcon = props => {
            const {icon, onClick, color, text, showText} = props,
              buttonIconContainerClass = classnames('button-icon-container', {
                active: showText
              });
          
            return (
              <div
                className={buttonIconContainerClass}
                onClick={onClick}
                style={{borderColor: color}}>
                <div className={`icon-container ${icon}`}></div>
                <div
                  className="text-container"
                  style={{display: showText ? '' : 'none'}}>{text}</div>
              </div>
            );
          }
          
          ButtonIcon.propTypes = {
            icon: React.PropTypes.string.isRequired,
            onClick: React.PropTypes.func.isRequired,
            color: React.PropTypes.string,
            text: React.PropTypes.string,
            showText: React.PropTypes.bool
          }
          
          export default ButtonIcon;
          

          我的测试:

          it('should call onClick prop when clicked', () => {
            const iconMock = 'test',
              clickSpy = jasmine.createSpy(),
              wrapper = ReactTestUtils.renderIntoDocument(<div><ButtonIcon icon={iconMock} onClick={clickSpy} /></div>);
          
            const component = findDOMNode(wrapper).children[0];
          
            ReactTestUtils.Simulate.click(component);
          
            expect(clickSpy).toHaveBeenCalled();
            expect(component).toBeDefined();
          });
          

          重要的是包装组件:

          <div><ButtonIcon icon={iconMock} onClick={clickSpy} /></div>
          

          希望对您有所帮助!

          【讨论】:

            最近更新 更多