首页 > 解决方案 > 如何使用反应、笑话和酶对自定义表格单元格渲染器功能进行单元测试?

问题描述

我想测试一个返回 JSX 内容的 React 类组件函数。下面是我的代码:

产品组成

export default class Products extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      products: [],
    };
  }

  iconRenderer = (data) => {
    return (
      <i
        className="fa fa-qrcode fa-3x"
      >
      </i>
    );
  };

  getDisplayColumns = () => {
    return [
      {
        fieldName: 'producticon',
        displayText: 'Name',
        isEditable: false,
        visible: true,
        columnSize: 2,
        renderer: this.iconRenderer
      }
    ];
  };

  render() {
    const displayColumns = this.getDisplayColumns();
    return (
      <div className=''>
            {this.state.products && this.state.products.length > 0 &&
            <CustomTableGeneric
              tableId="item-list-table"
              data={this.state.products}
              columns={displayColumns}
            >
            </CustomTableGeneric>
            }
          </div>
    );
  }
}

CustomTableGeneric组件(我试图简化代码)

export default class CustomTableGeneric extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            currentTableData: [],
            columnsToDisplay: [],
        };

        this.renderRows = this.renderRows.bind(this);
        this.renderIndividualRow = this.renderIndividualRow.bind(this);
    }

    renderIndividualRow(data, dataKeys) {
        return dataKeys.map((item, index) => {
            let currentRowId = data['id'];
            let columnWidth = this.state.columnsToDisplay[index].columnSize;
            if (item.renderer) {
                    return (
                        <Col md={columnWidth} className="table-column-cell"
                             key={index}>
                            {item.renderer(data, item.fieldName)}
                        </Col>
                    );
                } else {
                    return (
                        <Col md={columnWidth} className="table-column-cell" key={index}>{data[item.fieldName]}</Col>
                    );
                }
        });
    }

    renderRows() {
        let dataKeys = clonedeep(this.state.columnsToDisplay);
        let dataRows = clonedeep(this.state.currentTableData);
        if (dataRows.length > 0) {
            return dataRows.map((row, index) => {
                return (
                        <Row key={index} className="table-row">
                            {this.renderIndividualRow(row, dataKeys)}
                        </Row>
                    );
            });
        }
    }

    render() {
        return (
            <Row id={this.props.tableId}>
                <Col className="custom-table">
                    <Row className="table-header">
                        {this.renderHeaders()}
                    </Row>
                    {this.renderRows()}
                </Col>
            </Row>
        );
    }
}

CustomTableGeneric.propTypes = {
    tableId: PropTypes.string,
    data: PropTypes.arrayOf(PropTypes.object).isRequired,
    columns: PropTypes.arrayOf(PropTypes.shape({
        fieldName: PropTypes.string,
        displayText: PropTypes.string,
        renderer: PropTypes.func,
    })).isRequired,
};

Products.test.js

import React from 'react';
import {shallow, mount} from 'enzyme';

import CustomTableGeneric from '../../components/CustomTableGeneric';
import Products from './Products';

const props = {
  id: '123'
};

describe('Products function tests', () => {
  it('should call the iconRenderer function', () => {
    const wrapper = shallow(<Products {...props} />);
    const result = wrapper
        .instance()
        .iconRenderer();
    console.log(result);

  });
});

下面是我运行测试时的控制台输出。

{ '$$typeof': Symbol(react.element),
      type: 'i',
      key: null,
      ref: null,
      props: { className: 'fa fa-qrcode fa-3x' },
      _owner: null,
      _store: {} }

如您所见,如果我从测试中显式调用iconRenderer(),它正在执行。但是我要测试的是检查在渲染Products组件时是否调用了iconRenderer() 。请看我如何在渲染函数中调用它,例如 Products render() -> getDisplayColumns() -> CustomTableGeneric() -> iconRenderer()。

下面是我要运行的实际测试

describe('Products function tests', () => {
  it('should call the iconRenderer function', () => {
    const wrapper = shallow(<Products {...props} />);
    jest.spyOn(wrapper.instance(), 'iconRenderer');
    expect(wrapper.instance().iconRenderer()).toHaveBeenCalled();
  });
});

但我得到以下错误

Error: expect(jest.fn())[.not].toHaveBeenCalled()

jest.fn() value must be a mock function or spy.
Received:
  object: <i className="fa fa-qrcode fa-3x" />

任何建议都受到高度赞赏。

标签: reactjsunit-testingjestjsenzyme

解决方案


在我看来,您的代码有两个问题:

  1. 您无意中调用了 in 中的模拟函数expect,这会导致错误:expect正在接收模拟的返回值iconRenderer,而不是模拟本身。将您的行更改expect为:

    expect(wrapper.instance().iconRenderer).toHaveBeenCalled();
    
  2. 您需要iconRenderer在渲染之前进行模拟Product,因此在渲染时调用它Product。因此,需要在此行之前对其进行模拟:

    const wrapper = shallow(<Products {...props} />);
    

    您可以在这个问题中看到如何做到这一点的示例。


推荐阅读