反应警告:失败的上下文类型:在`Component`中未指定必需的上下文`router`

时间:2015-05-14 12:04:36

标签: reactjs react-router jestjs

我正在尝试测试一个React组件,该组件需要与app.js分开使用react-router。

我有一个使用mixin Router.Navigation进行重定向的组件,如下所示:

router

我试图为此写一个测试,但碰到了一堵墙。除了我无法测试转换以便按预期工作之外,我在Jest测试中也遇到了这条错误消息:

警告:上下文类型失败:Searchbar未指定必需的上下文https://www.google.com/finance/converter?a=1&from=INR&to=USD&jsoncallback=?

有谁知道如何摆脱警告和奖金问题,我如何测试转换是否按预期工作?

我已经在这里和Github上的这个对话进行了研究:http://cdnjs.com/libraries/jquery-ui-timepicker-addon是我发现的最接近问题的人。看起来我需要单独导出路由器,但是在没有警告的情况下运行组件测试似乎需要很多开销https://github.com/rackt/react-router/issues/400

这真的是要走的路吗?

4 个答案:

答案 0 :(得分:5)

在React Router的0.13版本中,不推荐使用mixins NavigationState。相反,它们提供的方法存在于对象this.context.router上。这些方法不再被弃用,但如果您明确使用this.context.router,则不需要mixin(但您需要直接声明contextTypes);或者,您可以使用mixin,但不需要直接使用this.context.router。 mixin方法将为您访问它。

在任何一种情况下,除非您通过React Router渲染组件(通过Router#run),否则router对象不会提供给上下文,当然您也无法调用转换方法。警告告诉您的是 - 您的组件希望将路由器传递给它,但它无法找到它。

要单独测试它(不创建路由器对象或通过Router#run运行组件),您可以在组件的上下文中放置一个模拟的router对象,在正确的位置,并测试您使用正确的值调用transitionTo

答案 1 :(得分:4)

这是我的Jest文件,可以完整回答这个问题。 BinaryMuse的最后一段让我走在正确的轨道上,但我发现代码示例总是最有帮助的,所以这里是供将来参考。

jest.dontMock('./searchbar');

describe('Searchbar', function() {
  var React = require('react/addons'),
      Searchbar = require('../../components/header/searchbar'),
      TestUtils = React.addons.TestUtils;

  describe('render', function() {
    var searchbar;

    beforeEach(function() {
      Searchbar.contextTypes = {
        router: function() {
          return {
            transitionTo: jest.genMockFunction()
          };
        }
      };

      searchbar = TestUtils.renderIntoDocument(
        <Searchbar />
      );
    });

    it('should render the searchbar input', function() {
      var searchbarContainer = TestUtils.findRenderedDOMComponentWithClass(searchbar, 'searchbar-container');

      expect(searchbarContainer).toBeDefined();
      expect(searchbarContainer.props.children.type).toEqual('input');
    });

  });

});

希望将来可以帮助其他人。

答案 2 :(得分:3)

因为router严重依赖于React鲜为人知的context功能,所以你需要像here

所描述的那样对其进行存根
var stubRouterContext = (Component, props, stubs) => {
  return React.createClass({
    childContextTypes: {
      makePath: func,
      makeHref: func,
      transitionTo: func,
      replaceWith: func,
      goBack: func,
      getCurrentPath: func,
      getCurrentRoutes: func,
      getCurrentPathname: func,
      getCurrentParams: func,
      getCurrentQuery: func,
      isActive: func,
    },

    getChildContext () {
      return Object.assign({
        makePath () {},
        makeHref () {},
        transitionTo () {},
        replaceWith () {},
        goBack () {},
        getCurrentPath () {},
        getCurrentRoutes () {},
        getCurrentPathname () {},
        getCurrentParams () {},
        getCurrentQuery () {},
        isActive () {},
      }, stubs);
    },

    render () {
      return <Component {...props} />
    }
  });
};

并使用如下:

var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);

答案 3 :(得分:2)

我的答案不是Jest特定的,但它可能会帮助人们遇到同样的问题。 我创建了一个包装router context的类。

然后在你的测试中添加
<ContextWrapper><YourComponent/></ContextWrapper>

包装ReactIntl之类的其他内容会很有用。

请注意,您将失去使用浅层渲染的可能性,但ReactIntl已经是这种情况。

希望能有所帮助。

ContextWrapper.js

import React from 'react';

export default React.createClass({
    childContextTypes: {
        router: React.PropTypes.object
    },

    getChildContext () {
        return {
            router: {}
        };
    },

    render () {
        return this.props.children;
    }
});