【问题标题】:React/Redux Testing w/ Enzyme带酶的 React/Redux 测试
【发布时间】:2017-05-03 18:21:13
【问题描述】:

我正在学习如何使用酶测试 React/Redux 组件。该组件将应用级状态作为道具。当我运行测试时,我得到了错误:

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).

TypeError: Cannot read property 'contextTypes' of undefined

在下面的测试文件中,我的wrapper 的console.log 记录为undefined

我知道我的设置有问题,并花了几个小时试图解决这个问题。任何人都可以在我导入和尝试使用该组件的方式中看到任何明显的东西吗?我不知道为什么是undefined。提前感谢您的任何帮助或见解!

BackendDisplay.js

import React from 'react';
import { connect } from 'react-redux';
import moment from 'moment';

var BackendDisplay = React.createClass({

  render() {

    const { username, node_version, app_path, timestamp } = this.props.loginState;
    const dateTime = moment(timestamp).format('MMMM Do YYYY, h:mm:ss a');

    return (
      <div>
        <h1>Welcome, {username}!</h1>
        <p><span className="bold">Node Version:</span> {node_version}</p>
        <p><span className="bold">Application Path:</span> {app_path}</p>
        <p><span className="bold">Date/Time:</span> {dateTime}</p>
      </div>
    );
  }
});

const mapStateToProps = function(store) {
  return store;
}

module.exports = connect(mapStateToProps)(BackendDisplay);

BackendDisplay.test.js

'use strict';

import React from 'react';
import {shallow} from 'enzyme';
import { connect } from 'react-redux';
import { BackendDisplay } from '../components/BackendDisplay';

describe('<BackendDisplay />', () => {

  it('Correctly displays username, node_version, app_path, and timestamp', () => {

    const wrapper = shallow(<BackendDisplay />);
    console.log(wrapper);

  });

});

修改后编辑: BackendDisplay.js

import React from 'react';
import { connect } from 'react-redux';
import moment from 'moment';

var BackendDisplay = React.createClass({

  render() {

    const { username, node_version, app_path, timestamp } = this.props.loginState;
    const dateTime = moment(timestamp).format('MMMM Do YYYY, h:mm:ss a');

    return (
      <div>
        <h1>Welcome, {username}!</h1>
        <p><span className="bold">Node Version:</span> {node_version}</p>
        <p><span className="bold">Application Path:</span> {app_path}</p>
        <p><span className="bold">Date/Time:</span> {dateTime}</p>
      </div>
    );
  }
});

const mapStateToProps = function(store) {
  return store;
}

// module.exports = connect(mapStateToProps)(BackendDisplay);
export default connect(mapStateToProps)(BackendDisplay);

BackendDisplay.test.js

'use strict';

import React from 'react';
import {shallow} from 'enzyme';
import { connect } from 'react-redux';
import store from '../store';
import { Provider } from 'react-redux';
import ConnectedBackendDisplay, {BackendDisplay} from '../components/BackendDisplay';

describe('<BackendDisplay />', () => {

  it('Correctly displays username, node_version, app_path, and timestamp', () => {

    const wrapper = shallow(
      <Provider store={store}>
        <BackendDisplay />
      </Provider>
    );

    console.log(wrapper.find(BackendDisplay));
    expect(wrapper.find(BackendDisplay).length).to.equal(1);

  });

});

错误信息: TypeError: Enzyme::Selector expects a string, object, or Component Constructor

【问题讨论】:

    标签: javascript unit-testing reactjs redux enzyme


    【解决方案1】:

    您的 BackendDisplay 是一个容器组件,它通过使用 connect api 连接到 Redux 存储。

    您应该导出未修饰的组件以进行测试。因为它是未修饰的,所以这个导出的组件不会被 react-redux 的 Connect 组件包裹。

    var BackendDisplay = React.createClass({
    
      render() {
    
        const { username, node_version, app_path, timestamp } = this.props.loginState;
        const dateTime = moment(timestamp).format('MMMM Do YYYY, h:mm:ss a');
    
        return (
          <div>
            <h1>Welcome, {username}!</h1>
            <p><span className="bold">Node Version:</span> {node_version}</p>
            <p><span className="bold">Application Path:</span> {app_path}</p>
            <p><span className="bold">Date/Time:</span> {dateTime}</p>
          </div>
        );
      }
    });
    

    然后你可以按如下方式导入,让测试生效

    import {BackendDisplay} from 'BackendDisplay'
    

    作为奖励,您还可以通过更改以下行来导出装饰的 BackendDisplay 组件

    module.exports = connect(mapStateToProps)(BackendDisplay);
    

     export default connect(mapStateToProps)(BackendDisplay);
    

    这是导入装饰组件和未装饰组件的方法

    import ConnectedBackendDisplay, {BackendDisplay} from 'BackendDisplay'  
    

    ConnectedBackendDisplay 指的是被装饰的组件,它是 通过无名导出方式导出(导出默认BackendDisplay)。

    我们只是给它起这个名字,以便它清楚地被包装在一个连接组件中。

    我已更新以下组件以使用导出默认值,它提供未命名的导出。

    后端显示

    import React from 'react';
    import { connect } from 'react-redux';
    import moment from 'moment';
    
    export const BackendDisplay = React.createClass({
    
      render() {
    
        const { username, node_version, app_path, timestamp } = this.props;
        // removed reference to this.props.loginState
    
        const dateTime = moment(timestamp).format('MMMM Do YYYY, h:mm:ss a');
    
        return (
          <div>
            <h1>Welcome, {username}!</h1>
            <p><span className="bold">Node Version:</span> {node_version}</p>
            <p><span className="bold">Application Path:</span> {app_path}</p>
            <p><span className="bold">Date/Time:</span> {dateTime}</p>
          </div>
        );
      }
    });
    
    const mapStateToProps = function(store) {
      return store;
    }
    
    export default connect(mapStateToProps)(BackendDisplay);
    

    这是一个测试套件,用于演示使用酶测试上述组件作为装饰组件和未装饰组件。

    我正在使用 chai 库来简化测试断言。 jsdom 库也被用于创建 DOM 环境,因此我们可以使用 Enzyme 的 mount 函数来测试组件,该函数可以完全渲染组件。

    测试

    'use strict';
    import React from 'react';
    import jsdom from 'jsdom'
    import { expect } from 'chai'
    import { shallow , mount} from 'enzyme';
    import { Provider } from 'react-redux';
    import ConnectedBackendDisplay, // decorated component
       {BackendDisplay} from 'app/components/BackendDisplay';  // undecorated component
    // for mocking a store to test the decorated component
    import configureMockStore from 'redux-mock-store'; 
    
    // create a fake DOM environment so that we can use Enzyme's mount to
    // test decorated components
    const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
    global.document = doc
    global.window = doc.defaultView
    
    
    describe.only('<BackendDisplay />', () => {
    
        it('undecorated component correctly displays username', () => {
            // define the prop we want to pass in
            const username = 'Foo'
            // render the component with the prop
            const wrapper = mount(<BackendDisplay username={username} />);
            // test that the text of the first <p> equals username prop that we passed in       
            expect(wrapper.find('h1').first().text()).to.equal(username);
       });
    
        it('decorated component correctly displays username', () => {
            // define the prop we want to pass in
            const username = 'Foo'
            const initialState = { }
            // create our mock store with an empyty initial state
            const store = configureMockStore(initialState)
    
            // render the component with the mockStore
            const wrapper = shallow(<Provider store={store}>
                                    <ConnectedBackendDisplay username={username}/>
                                  </Provider>);
    
            // test that the text of the first <p> equals username prop that we passed in       
            expect(wrapper.find('h1').first().text()).to.equal(username);
       });
    });
    

    【讨论】:

    • 感谢您的快速回复!这是测试连接到商店的组件的标准方法吗?您通常会为未修饰的组件创建一个单独的文件吗?如果是,您会将这个文件放在哪里?感谢您帮助测试新手 :)
    • 我会将其添加到我的答案中。总之,这个组件的测试都在同一个测试文件中。
    • 哎呀。各种新错误。今天早上奋斗巴士。
    • 警告:React.createElement:类型不应为 null、未定义、布尔值或数字。它应该是一个字符串(对于 DOM 元素)或一个 ReactClass(对于复合组件)。
    • 警告:失败的道具类型:道具storeProvider中标记为必填,但其值为undefined。在提供者中(在 BackendDisplay.test.js:15)
    猜你喜欢
    • 2019-01-07
    • 2018-04-12
    • 1970-01-01
    • 2018-06-14
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-15
    • 2016-08-20
    相关资源
    最近更新 更多