Redux: 内に連結成分がネストされている連結成分のテスト

作成日 2016年11月10日  ·  21コメント  ·  ソース: reduxjs/redux

こんにちは、これは問題というよりも質問です。

最近、プロジェクトでReactとReduxの使用を開始しました。テストでいくつか問題が発生していますが、ドキュメントに問題の決定的な解決策が記載されていないようです。

現在、いくつかのユニットテストを記述したい連結成分がありますが、最初の成分内にネストされた連結成分があり、の小道具または状態(またはほとんどの場合両方)によってブロックされてしまうようです。ネストされたコンポーネントが設定されていません。

テストしようとしているコンポーネントだけに集中できるように、1つのコンポーネントをスタブする方法があるかどうか疑問に思いました。

前もって感謝します

question

最も参考になるコメント

ここで提起されている懸念は、テスト対象のコンポーネントが、直接の子として、または階層のどこかで、他の接続されたコンポーネントをレンダリングする場合です。 mount()を介して完全なレンダリングを行うと、子孫の連結成分は絶対にthis.context.storeにアクセスしようとしますが、見つかりません。 したがって、主なオプションは、浅いテストを実行して子孫がレンダリングされないようにするか、実際にストアをコンテキストで使用できるようにすることです。

全てのコメント21件

2つのこと:

1)これは使用法であり、おそらくスタックオーバーフローに行くはずです。

2) connect高階コンポーネントを使用する便利さの一部
unconnectedコンポーネントの単体テストを書くだけで、
reduxを信頼して、店舗の配管を適切に処理します

それが何を意味するのかわからない場合は、例を投稿できます。

いくつかの考え:

  • 1つのコンポーネントだけに焦点を当てる1つの方法は、実際には子をレンダリングしない「浅い」レンダリングを使用することです。
  • テストで<Provider store={testStore}><ConnectedComponent /></Provider>をレンダリングすることもできます

参考までに、ここにReact / Reduxテストに関する多数の記事へのリンクがあります。https

そして、はい、使用法の質問として、これは他の場所で本当によく尋ねられます。

ありがとう@markerikson私は現在あなたが言及した後者の方法を試していますが、リンクのリストもブックマークします:)

一度コンポーネントを分離してテストしたい場合は、次のようなことを行う方がよいと思います。

// Component.js
import React from 'react'

export const Component = ({ a, b, c }) => ( 
  // ...
)

// ...

export default connect(mapStateToProps, mapDispatchToProps)(Component)

次に、次のようにテストで接続する前にコンポーネントを取り込むことができます。

// Component.spec.js
import { Component } from './Component.js'
import { mount } from 'enzyme'

it('should mount without exploding', () => {
  mount(<Component a={1} b={2} c={3} />)
})

本当にテストしたいのが、ストアへの変更がProviderをレンダリングするのが理にかなっているよりも正しく伝播しているということである場合、それは異なりますが、それをテストすることで多くの価値が得られるかどうかはわかりません。

ここで提起されている懸念は、テスト対象のコンポーネントが、直接の子として、または階層のどこかで、他の接続されたコンポーネントをレンダリングする場合です。 mount()を介して完全なレンダリングを行うと、子孫の連結成分は絶対にthis.context.storeにアクセスしようとしますが、見つかりません。 したがって、主なオプションは、浅いテストを実行して子孫がレンダリングされないようにするか、実際にストアをコンテキストで使用できるようにすることです。

@markeriksonああ、私はちょうど

これまでの皆さんの助けに感謝します、

現在私が抱えている主な問題は、ネストされている接続されたコンポーネントに状態を渡せないように見えるため、ビューが正しくレンダリングされないことです。

一般的にモック状態は私が実際にかなり苦労していて、主題について決定的なものを見つけることができなかったものですので、誰かが私がモック状態をどのように渡すかについて私にいくつかのアイデアを与えることができれば素晴らしいですネストされた接続または高次コンポーネント?

TIA

@StlthyLee :「状態を渡す」とはどういう意味ですか? テストで<Provider store={store}><ComponentWithConnectedDescendants /></Provider>をレンダリングすると、それで問題が解決するはずです。

それが問題です。それを行うだけでは正しく処理されません。内部にネストされたコンポーネントには、アプリケーションの状態から特定のパラメーターが必要ですが、現在、何らかの理由で取得されていません。

@StlthyLeeは、おそらく

@Koleokうまくいけば、これは明確にするのに役立ちます

https://gist.github.com/StlthyLee/02e116d945867a77c382fa0105af1bf3

質問しない場合は、これは私が根底に行きたいと思っていることです。過去9〜10年間、Railsの世界で働き、その観点からTDDをよく理解してきたので、私は今、 React / Reduxの世界でTDDの周りに頭を抱えています。

だから私は今この問題の根底にあると信じています、それはそれほどテストの問題ではありませんでしたが、別のチームメンバーによって作られたコピーと過去のエラーでした、これは書く必要があることの一部であると思います私の好みのTDDの方法ではなく、コードに合うようにテストします。 ここに記載されているすべての情報に感謝します。

いくつかのユースケースで機能する可能性のある別のオプションを提供するために、最近、ネストされた接続コンポーネントでこの問題に遭遇しました。 モックストアを作成するのではなく、各コンポーネントの接続されていないバージョンをエクスポートして、ストアなしでユニットテストを行いました。 このようなもの:

class TopLevelImpl extends React.PureComponent {
  // Implementation goes here
}

// Export here for unit testing.  The unit test imports privates and uses TopLevel
// with Enzyme's mount().
export const privates = {
  TopLevel: TopLevelImpl,
}

export const TopLevel = connect(mapStateToProps)(TopLevelImpl)

子のネストされたコンポーネントに対して同じことを行ったので、それぞれを個別にテストできます。

次に、最上位の連結成分を取得し、子連結成分をアプリのrender()関数の小道具として渡しました。 ただし、トップレベルコンポーネントを単体テストするときは、接続されたコンポーネントを渡す代わりに、モックコンポーネントを渡して、適切な場所にレンダリングされることを確認します。 ただし、接続されていないコンポーネントを渡すこともできます。

このようなもの:

// Root render function
render() {
  return (
    <Provider store={store}>
      <TopLevel child1={<Child1 />} child2={<Child2 />} />
    </Provider>
  )
}

私は実際にあなたの考えが好きです。 しかし、私は代わりにこのようなことをします

const props = {
  child1: () => (<div />),
  child2: () => (<div />)
}
<TopLevel {...props} />

すでにchild1とchild2をテストしている場合は、TopLevelコンポーネントにそれらを含める必要はおそらくないと思います。

コンポーネントを条件付きでレンダリングする必要がある場合は、関数も渡します。

しかし、私はあなたの最後の文には従いません。 テストは、子コンポーネントを別のコンポーネントに渡すことと何の関係がありますか?

上手。 たとえば、1つまたはいくつかの内部接続コンポーネントを使用する外部接続コンポーネントがあります。 この場合、外部コンポーネントをテストしたい場合は、次のようにします。

wrapper = mount(<ExternalComponent.WrappedComponent {...props} />)

ただし、内部コンポーネントを正しく実行するには状態エラーが発生しました。

Invariant Violation: Could not find "store" in either the context or props

それからモック状態などを通過させてみました。うまくいきました。 ただし、私の場合、非同期呼び出し、initialStateなど、接続されているすべてのコンポーネントに対して多くの初期化を行う必要があり、それに関連する他のエラーが発生しました。

外部コンポーネントをテストしたいだけの場合、これはすべての内部接続コンポーネントに対してすべての適切な初期化を行うのはやり過ぎだと思います。

そこで、接続されているすべてのコンポーネントそれらを空のdiv

そうそう、それがパターンの本来の動機でした。 つまり、文を変更するということは、「child1とchild2をすでにテストしている場合は、単体テスト時にそれらをTopLevelコンポーネントに渡す必要はないと思います」という意味です。

同意しました。

@StlthyLee 、残念ながら、連結成分がネストされているコンポーネントをテストしようとしたときに、同じ問題に直面しました。

私が特に役立つと思った解決策は、テスト環境に応じて、ネストされたコンポーネントをreduxストアに接続することです。

  • テストが実行されたら、環境変数NODE_ENV="test"設定します。 この変数をキャッチして、コンポーネントにデフォルトのプロパティを受け取るようにすることができます。 このような場合、コンポーネントはストアに接続されておらず、親コンポーネントをテストしても問題は発生しません。
  • それ以外の場合は、コンポーネントを接続します。 これは、アプリケーションでコンポーネントを実行するときのデフォルトのシナリオです。

a)まず、環境がtestかどうかを判断するヘルパー関数が必要です。

export default function ifTestEnv(forTestEnv, forNonTestEnv) {
  const isTestEnv = process && process.env && process.env.NODE_ENV === 'test';
  const hoc = isTestEnv ? forTestEnv : forNonTestEnv;
  return function(component) {
    return hoc(component);
  };
}

b)次に、 defaultProps()connect()は、環境に応じて条件付きで適用する必要があります。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import defaultProps from 'recompose/defaultProps';

import ifTestEnv from 'helpers/if_test_env';
import { fetch } from './actions';

class MyComponent extends Component {
   render() {
      const { propA, propB } = this.props;
      return <div>{propA} {probB}</div>
   }

   componentDidMount() {
      this.props.fetch();
   }
}

function mapStateToProps(state) {
  return {
    propA: state.valueA,
    propB: state.valueB
  };
}

export default ifTestEnv(
  defaultProps({
    propA: 'defaultA',
    propB: 'defaultB',
    fetch() {   }
  }),
  connect(mapStateToProps, { fetch })
)(MyComponent);

c)テストを実行するときは、 NODE_ENV"test"ことを確認してください。

  • mocha CLIを使用する場合は、プレフィックスを付けてテスト環境を設定します: NODE_ENV='test' mocha app/**/*.test.jsx
  • webpackの場合、プラグインを適用します。
plugins: [
    // plugins...
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('test')
      },
    })
]

@ markerikson2つの提案をありがとう。 2つ目を試し、コンポーネントをラップしてプロバイダーでテストしました。 この場合、 mapStateToPropsは正しく機能しますが、 mapDispatchToProps期待どおりの結果が得られません。 ここで、テスト対象のコンポーネントは、他の接続されたコンポーネントをレンダリングします。

私のmapDispatchToPropsは次のようになります

/* <strong i="10">@flow</strong> */
import { bindActionCreators } from 'redux';

import type { Dispatch } from './types';
import * as actions from './actions';

export default (dispatch: Dispatch, ownProps: Object): Object => ({
  actions: bindActionCreators(actions, dispatch),
});

ここで返されるアクションの値はundefinedです。

store.js

import { applyMiddleware, compose, createStore } from 'redux';
import { autoRehydrate } from 'redux-persist';
import thunk from 'redux-thunk';

import rootReducer from './reducers';
import { REHYDRATE } from 'redux-persist/constants';

const store = compose(autoRehydrate(), applyMiddleware(thunk, createActionBuffer(REHYDRATE)))(createStore)(rootReducer);

export default store;

これはかなり古いスレッドだと思いますが、これは少し厄介な問題であることがわかり、この解決策がこのスレッドに出くわした人(私のような)に役立つかもしれないと思いました。

私は実際にReduxに関連するものをテストしていなかったので、私が見つけた簡単な解決策は、接続関数をモックして、渡された変更されていないコンポーネントだけを返すことでした。

たとえば、アプリケーションには次のコンポーネントがあります。


export class ExampleApp extends Component {
  render() {
  }
}

export default connect(
  null,
  { someAction }
)(ExampleApp);

次に、テストファイルの先頭に、モック接続関数を配置して、Reduxがコンポーネントと対話するのを停止できます。

jest.mock("react-redux", () => {
  return {
    connect: (mapStateToProps, mapDispatchToProps) => (
      ReactComponent
    ) => ReactComponent
  };
});

このモックは、接続された子をマウントするすべてのテストファイルに配置する必要があります。

@scottbanyardどのようにして小道具を子コンポーネントに渡しますか?

このページは役に立ちましたか?
0 / 5 - 0 評価