如何正确测试 React 组件方法

How to properly test react component methods

为了我的工作,我已经在 React 中进行了一段时间的开发,但最近我被要求使用 Istanbul 使一些应用程序的测试覆盖率达到 ~100%。在过去的几天里,我已经为这个应用程序编写了超过 160 个测试,但我无法覆盖我代码的某些部分。我在涵盖 AJAX 调用、setTimeout 回调和需要另一个组件才能正常运行的组件方法方面遇到了最大的麻烦。 我已经阅读了几个 SO 问题但无济于事,我相信那是因为我不正确地处理了这个问题。我正在使用 Enzyme、Chai assertions、Mocha、Istanbul coverage、sinon for spies,并且正在考虑 nock,因为我无法让 sinon fakeServer 工作。

这里是有问题的组件方法:

_getCategoriesFromServer() {
const _this = this;
sdk.getJSON(_this.props.sdkPath, {
    itemsperpage: 10000
}).done(function(data) {
    _this.setState({
        isLoaded: true,
        categories: _this.state.categories.concat(data)
    });
});

}

这是该组件的测试:

 it('should call _getCategoriesFromServer', () => {
    sinon.spy(CategoryTree.prototype, '_getCategoriesFromServer');
    wrapper = mount(<CategoryTree {...props} />);
    expect(CategoryTree.prototype._getCategoriesFromServer.calledOnce).to.be.true;
});

sdk 只是一个使用getJSON 构造jQuery API 调用的模块。 我的测试覆盖了函数调用,但没有覆盖此处看到的 .done 回调: 所以我的问题是,如何正确测试 .done? 如果有人有文章、教程、视频以及任何解释如何正确测试组件方法的内容,我将不胜感激!

第二个问题是,如何测试一个作为 prop 传递给子组件的方法?根据测试覆盖率要求,我必须对该方法进行测试,但它的唯一目的是传递给子组件以用作 onClick。这很好,但是 onClick 依赖于另一个 AJAX 调用返回子组件中的数据。 我最初的冲动是只使用酶 .find 来定位那个 onClick 并模拟一个点击事件,但是带有那个 onClick 的元素不存在,因为 AJAX 调用没有在测试环境中带回数据。 如果你已经读到这里,我向你致敬。如果你能帮忙,我谢谢你!

您可以像这样使用 rewire(https://github.com/jhnns/rewire) 来测试您的组件:

// let's said your component is ListBox.js
var rewire = require("rewire");
var myComponent = rewire("../components/ListBox.js");

const onDone = sinon.spy()
const sdkMock = {
    getJSON (uri, data) {
       return this.call('get', uri, data);
    },
    call: function (method, uri, data) {
       return { done: function(){ onDone() } }
    }
};
myComponent.__set__("sdk", sdkMock);

最后你将测试 done 函数是否像这样被调用:

expect(onDone.calledOnce)to.be.true

有了这个应该可以按预期工作。如果您需要更多选项,您可以在 GitHub.

中看到所有 rewire 选项

巴贝尔

如果你使用 babel 作为转译器,你需要使用 babel-plugin-rewire(https://github.com/speedskater/babel-plugin-rewire) 你可以这样使用它:

sdk.js

function call(method, uri, data) {
   return fetch(method, uri, data);
}
export function getJSON(uri, data) {
   return this.call('get', uri, data);
}

yourTest.js

import { getJSON, __RewireAPI__ as sdkMockAPI } from 'sdk.js';

describe('api call mocking', function() {
   it('should use the mocked api function', function(done) {
      const onDone = sinon.spy()
      sdkMockAPI.__Rewire__('call', function() {
         return { done: function(){ onDone() } }
      });
      getJSON('../dummy.json',{ data: 'dummy data'}).done()
      expect(onDone.calledOnce)to.be.true
      sdkMockAPI.__ResetDependency__('call')
   })
})