如何用 enzyme, chai 测试 HOC

How to test HOC with enzyme, chai

我有一个 HOC 函数,它接收一个 React 组件和 returns 用两个新方法属性(handleBackmoveitOnTop)对组件做出反应,如下所示:

import React, { Component } from "react";
import classNames from "classnames";

 export default WrappedComponent => {
  return class extends Component {
      constructor(props) {
          super(props);
            this.moveitOnTop = this.moveitOnTop.bind(this);
            this.handleBack = this.handleBack.bind(this);

        this.state = {
            isSearchActive: false
        };
    }
    moveitOnTop(flag) {
        this.setState({ isSearchActive: flag });
        window.scrollTo(0, -100);
    }

    handleBack() {
        this.setState({ isSearchActive: false });
        if (document.body.classList.contains("lock-position")) {
            document.body.classList.remove("lock-position");
        }
    }

    render() {
        const props = {
            ...this.props,
            isSearchActive: this.state.isSearchActive,
            moveitOnTop: this.moveitOnTop,
            goBack: this.handleBack
        };

        const classes = classNames({ "c-ftsOnTop": this.state.isSearchActive });
        return (
            <div className={classes}>
                <WrappedComponent {...props} />
            </div>
        );
    }
  };
 };

组件:

 //import fireAnalytics
import { fireAnalytics } from "@modules/js-utils/lib";
class MyComponent extender Component{
  constructor(){
     super(props);
     this.handleClick = this.handleClick.bind(this);
  }

   handleClick(e) {
     // calling analytics function by passing vals
    fireAnalytics({
        event: "GAEvent",
        category: "",
        action: `Clicked on the ${e.target.id} input`,
        label: "Click"
    });

     // I CALL THE HOC PROPERTY
     this.props.moveitOnTop(true);

     // I CALL THE HOC PROPERTY
     this.props.handleBack();
   }

  render(){
     return(
        <div className="emailBlock">
          <input type="text" onClick={handleClick} />
          <Button className="submit">Submit</Button>
        </div>
     )
  }
}

// export HOC component
export default hoc(MyComponent);

// export just MyComponent
export {MyComponent};

我想测试 HOC:

  1. 我需要检查 class .c-ftsOnTop 是否存在
  2. 我需要检查调用 this.props.handleBack & `this.props.moveitOnTop'
  3. onClick 函数
  4. 我需要检查 class名称 emailBlock 是否存在。

我试过但失败的测试:

 import { mount, shallow } from 'enzyme';
 import sinon from 'sinon';
 import React from 'react';
 import { expect } from 'chai';
 import hoc from '....';
 import {MyComponent} from '...';
 import MyComponent from '....';

 it('renders component', () => {

const props = {}
const HocComponent = hoc(MyComponent);
const wrapper = mount(
  <HocComponent {...props} />
);

console.log('wrapper:', wrapper);
expect(wrapper.find('.c-ftsOnTop')).to.have.lengthOf(1);
expect(wrapper.hasClass('c-fts-input-container')).toEqual(true);
 })

错误

AssertionError: expected {} to have a length of 1 but got 0

console.log: wrapper: ReactWrapper {}

任何人都可以帮助我如何渲染 HOC 吗?

这是一个工作测试:

import { mount } from 'enzyme';
import React from 'react';
import WrappedMyComponent from './MyComponent';

it('renders component', () => {
  const props = {}
  const moveitOnTopSpy = jest.spyOn(WrappedMyComponent.prototype, 'moveitOnTop');
  const handleBackSpy = jest.spyOn(WrappedMyComponent.prototype, 'handleBack');
  const wrapper = mount(
    <WrappedMyComponent {...props} />
  );

  // 1. I need to check that class .c-ftsOnTop exists
  wrapper.setState({ isSearchActive: true });  // <= set isSearchActive to true so .c-ftsOnTop is added
  expect(wrapper.find('.c-ftsOnTop')).toHaveLength(1);  // Success!

  // 2. I need to check onClick function that calls this.props.handleBack & `this.props.moveitOnTop'
  window.scrollTo = jest.fn();  // mock window.scrollTo
  wrapper.find('input').props().onClick();
  expect(moveitOnTopSpy).toHaveBeenCalled();  // Success!
  expect(window.scrollTo).toHaveBeenCalledWith(0, -100);  // Success!
  expect(handleBackSpy).toHaveBeenCalled();  // Success!

  // 3. I need to check if className emailBlock exists
  expect(wrapper.find('.emailBlock')).toHaveLength(1);  // Success!
})

详情

.c-ftsOnTop 仅在 isSearchActivetrue 时添加,因此只需设置组件的状态即可添加 class。

如果您在 moveitOnTophandleBack 的原型方法上创建间谍,那么当 hoc 通过将它们绑定到 this构造函数,实例方法将绑定到你的间谍。

window.scrollTojsdom 中默认将错误记录到控制台,因此您可以模拟它以避免该错误消息并验证它是否使用预期参数调用。


注意上面的测试需要在MyComponent中修复以下错别字:

  • extender 应该是 extends
  • constructor 应该采用 props 参数
  • onClick 应该绑定到 this.handleClick 而不是 handleClick
  • handleClick 应该调用 this.props.goBack() 而不是 this.props.handleBack()

(我猜 MyComponent 只是作为实际组件的示例放在一起)