在 ES7 中存根类字段函数



在我的测试套件中,如何存根类的属性,这是一个函数*?使用普通方法,使用Object.getOwnPropertyNames(component.prototype)和猴子修补每个找到的方法很容易,但是经过长时间的斗争,我还没有找到任何方法来提取通过分配给类字段创建的函数。

我的测试堆栈由 Jest with Jasmine2 和 babel 组成。

转译

的问题在于箭头函数属性(当然,正如预期的那样)分配给输出转译"类"的实例(当然,实际上是函数)。所以我看不出除了实例化这个对象之外有任何方法可以刺杀它们,对吗?下面是输入 es7 代码和 babel 输出的示例。但是我不是特别喜欢这个解决方案,似乎很笨拙。此解决方案的另一个缺点是我无法直接实例化组件的类。


(*) 这个问题的背景是单元测试在类似 es7 的类中编写的 React 组件,其中箭头函数分配给类的属性以用于自动绑定。

在为我正在工作的项目编写单元测试时,我遇到了同样的问题,我认为我有一个很好的模式来解决它。希望它有帮助:

上下文

下面是一个 React 组件的示例,该组件具有使用胖箭头符号定义的方法handleClick

import React, { Component } from 'react';
class Foo extends Component {
  componentWillMount() {
    this.handleClick();
  }
  handleClick = (evt) => {
    // code to handle click event...
  }
  render() {
    return (
      <a href="#" onClick={this.handleClick}>some foo link</a>
    );
  }
}

问题

如此链接中所述,Babel 将转译代码,以便 handleClick 方法仅在实例化后可用(检查生成的构造函数的第 31 行到第 33 行

这里的问题是,有时您需要在实例化类之前访问使用胖箭头表示法定义的方法。

例如,假设您正在为 componentWillMount 类方法编写单元测试,并且想要存根handleClick以便只测试所需的单元。但是现在您遇到了一个问题,因为您只能在实例化后访问handleClick并且componentWillMount方法将由React自动调用,作为其实例化生命周期的一部分。

溶液

以下是我如何应用一个简单的模式来解决这样的问题:

import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import Foo from './foo';
describe('Foo', () => {
  describe('componentWillMount method', () => {
    const handleClickStub = sinon.stub();
    class FooWrapper extends Foo {
      constructor(props) {
        super(props);
        this.handleClick = handleClickStub;
      }
    }
    it('should register a click event listener to the externalElement property', () => {
      handleClickStub.reset();
      mount(<FooWrapper />);
      expect(handleClickStub.calledOnce).to.be.true;
    });
  });
});

解释

我已经将原始Foo组件包装到一个FooWrapper中,初始化原始组件后,在其构造函数上,我将原始handleClick方法替换为存根版本,允许我对componentWillMount类进行属性测试。

由于 babel 通过 transform-class-properties 在类方法上转译箭头函数语法的方式,类方法不再绑定在原型上,而是绑定在实例上。

使用 Jest 19 的内置断言和 .spyOn 方法,这是我的解决方案:

import React from 'react';
import { shallow } from 'enzyme';
describe('MyComponent', () => {
  it('should spy properly', () => {
    const wrapper = shallow(<Component />);
    const wrapperInstance = wrapper.instance();
    const spy = jest.spyOn(wrapperInstance, 'functionName');
    wrapperInstance.functionName();
    expect(spy).toHaveBeenCalledTimes(1);
  })
});

最新更新