10

app.js とは別に、react-router を必要とする React コンポーネントをテストしようとしています。

次のように、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;

このためのテストを作成しようとしましたが、壁にぶつかりました。transitionTo が期待どおりに動作することをテストできないという事実とは別に、Jest テストで次のエラー メッセージが表示されました。

警告: 失敗したコンテキスト タイプ: 必要なコンテキストrouterが で指定されていませんSearchbar

警告とボーナスの質問を取り除く方法、移行が期待どおりに機能することをテストする方法を知っている人はいますか?

私はこれと Github でのこの会話について調査しました: https://github.com/rackt/react-router/issues/400は、私が見つけた問題に最も近いものです。ルーターを個別にエクスポートする必要があるようですが、https://github.com/rackt/react-router/blob/master/docs/guides/のように警告なしでコンポーネント テストを実行するだけでは、多くのオーバーヘッドが発生するようです。 testing.md

それは本当に進むべき道ですか?

4

4 に答える 4

5

React Router のバージョン 0.13 では、mixinNavigationStateが廃止されました。代わりに、それらが提供するメソッドは object に存在しますthis.context.router。メソッドは非推奨ではなくなりましたが、this.context.router明示的に使用している場合は mixin は必要ありません (ただし、contextTypes直接宣言する必要があります)。this.context.routerまたは、ミックスインを使用できますが、直接使用する必要はありません。mixin メソッドがそれにアクセスします。

いずれの場合も、コンポーネントを React Router 経由で ( 経由でRouter#run) レンダリングしない限り、routerオブジェクトはコンテキストに提供されず、もちろん遷移メソッドを呼び出すことはできません。これが警告の意味です。コンポーネントはルーターが渡されることを期待していますが、それを見つけることができません。

これを単独でテストするには (ルーター オブジェクトを作成したり、 を介してコンポーネントを実行したりせずにRouter#run)、モックrouterオブジェクトをコンポーネントのコンテキストの正しい場所に配置transitionToし、正しい値でそれを呼び出すことをテストできます。

于 2015-05-14T15:46:02.863 に答える
4

routerReactのあまり知られていない機能に大きく依存しているため、ここでcontext説明されているようにスタブする必要があります。

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);
于 2015-06-14T21:43:03.723 に答える
3

この質問に対する完全な回答の 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');
    });

  });

});

これが将来誰かに役立つことを願っています。

于 2015-07-19T08:15:26.390 に答える
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;
    }
});
于 2016-01-27T16:32:17.580 に答える