反应警告:上下文类型失败:未在“组件”中指定所需的上下文“路由器”

React Warning: Failed Context Types: Required context `router` was not specified in `Component`

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

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

var React = require('react'),
    Router = require('react-router');

var Searchbar = React.createClass({

  mixins : [Router.Navigation],

  searchTerm: function(e) {
    if (e.keyCode !== 13) {
      return;
    }

    this.context.router.transitionTo('/someRoute/search?term=' + e.currentTarget.value)
  },

  render: function() {
    return (
      <div className="searchbar-container">
        <input type="search" placeholder="Search..." onKeyDown={this.searchTerm} />
      </div>
    )
  }
});

module.exports = Searchbar;

我试图为此编写测试,但 运行 撞墙了。除了我无法测试 t运行sitionTo 是否按预期工作之外,我还在 Jest 测试中遇到了这个错误消息:

警告:上下文类型失败:未在 Searchbar 中指定所需的上下文 router

有谁知道我如何摆脱警告和奖励问题,如何测试 t运行sition 是否按预期工作?

我已经在 Github 上对此进行了研究:https://github.com/rackt/react-router/issues/400 is the closest I've found to the problem. It looks like I need to export the router separately but that seems like a lot of overhead to just run component tests without the warning a la https://github.com/rackt/react-router/blob/master/docs/guides/testing.md

真的是这样吗?

在 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

因为 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);

这是我对这个问题的完整回答的 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');
    });

  });

});

希望这对以后的其他人有所帮助。

我的回答不是 Jest-specific,但它可能会帮助遇到同样问题的人。 我创建了一个 class 来包装 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;
    }
});