测试 React 模态组件

Testing a React Modal component

本文关键字:组件 模态 React 测试      更新时间:2023-09-26

很抱歉,我一直在尝试通过单击按钮来测试关闭我的 React Modal。模态非常简单,我已经尝试了我能想到或找到的所有方法,但我仍然无法查询它的子级。

模态组件:

var React = require('react');
var Modal = require('react-bootstrap').Modal;
var Button = require('react-bootstrap').Button;
var MyModal = React.createClass({
  ...
  render: function() {
    return (
      <Modal className="my-modal-class" show={this.props.show}>
        <Modal.Header>
          <Modal.Title>My Modal</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          Hello, World!
        </Modal.Body>
        <Modal.Footer>
          <Button onClick={this.props.onHide}>Close</Button>
        </Modal.Footer>
      </Modal>
    );
  }
});

我的目标是测试单击该"关闭"按钮时是否触发onHide()函数。

我的测试文件:

describe('MyModal.jsx', function() {
  it('tests the Close Button', function() {
    var spy = sinon.spy();
    var MyModalComponent = TestUtils.renderIntoDocument(
      <MyModal show onHide={spy}/>
    );
    // This passes
    TestUtils.findRenderedComponentWithType(MyModalComponent, MyModal);
    // This fails
    var CloseButton = TestUtils.findRenderedDOMComponentWithTag(MyModalComponent, 'button');
    // Never gets here
    TestUtils.Simulate.click(CloseButton);
    expect(spy.calledOnce).to.be.true;
  });
});

无论我尝试什么,我似乎都找不到关闭按钮。

我使用 React Base Fiddle (JSX) 编写了一个 jsFiddle 来找出测试中发生了什么(我创建了自己的"间谍",在调用时只是记录到控制台)。

我发现你找不到你的按钮的原因是因为它不存在你期望它的地方。

Bootstrap 模态组件 (

<Modal/> ) 实际上包含在 React-Overlays 模态组件中(在代码中称为 BaseModal,它来自这里)。这反过来又渲染了一个名为 Portal 的组件,其渲染方法仅返回 null 。您尝试在其上查找渲染组件的正是此null值。

由于模态不是以传统的 React 方式渲染的,React 无法看到模态以便使用 TestUtils。一个完全独立的<div/>子节点放置在document体中,这个新<div/>用于构建模态。

因此,为了允许您使用 React 的TestUtils模拟点击(按钮上的点击处理程序仍然绑定到按钮的点击事件),您可以使用标准 JS 方法来搜索 DOM。按如下方式设置测试:

describe('MyModal.jsx', function() {
  it('tests the Close Button', function() {
    var spy = sinon.spy();
    var MyModalComponent = TestUtils.renderIntoDocument(
      <MyModal show onHide={spy}/>
    );
    // This passes
    TestUtils.findRenderedComponentWithType(MyModalComponent, MyModal);
    // This will get the actual DOM node of the button
    var closeButton = document.body.getElementsByClassName("my-modal-class")[0].getElementsByClassName("btn btn-default")[0];
    // Will now get here
    TestUtils.Simulate.click(CloseButton);
    expect(spy.calledOnce).to.be.true;
  });
});

该函数getElementsByClassName返回具有该类的元素集合,因此您必须从每个集合中获取第一个(在您的测试用例中,也是唯一的一个)。

您的测试现在应该通过 ^_^

这是我最终采用的解决方案。它以 React 渲染模态的方式存根,只渲染一个<div>

test-utils.js

var sinon = require('sinon');
var React = require('react');
var Modal = require('react-bootstrap').Modal;
module.exports = {
  stubModal: function() {
    var createElement = React.createElement;
    modalStub = sinon.stub(React, 'createElement', function(elem) {
      return elem === Modal ?
        React.DOM.div.apply(this, [].slice.call(arguments, 1)) :
        createElement.apply(this, arguments);
    });
    return modalStub;
  },
  stubModalRestore: function() {
    if (modalStub) {
      modalStub.restore();
      modalStub = undefined;
    } else {
      console.error('Cannot restore nonexistent modalStub');
    }
  }
};

modal-test.jsx

var testUtils = require('./test-utils');
describe('test a Modal!', function() {
  before(testUtils.stubModal);
  after(testUtils.stubModalRestore);
  it('renders stuff', function() {
    var MyModalComponent = TestUtils.renderIntoDocument(
      <MyModal show/>
    );
    // Do more stuff you normally expect you can do
  });
});