web-dev-qa-db-ja.com

React&Redux内のEnzymeを使用したネストされたコンポーネントのテスト

別の「接続されたコンポーネント」(つまり、SampleComponent)をマウントするコンポーネントcontainerがあります。 SampleComponentingでmountをテストしようとすると(componentDidMountが必要なため)、エラーが発生します。

不変の違反:コンテキストまたは「Connect(ContainerComponent)」の小道具のいずれにも「ストア」が見つかりませんでした。ルートコンポーネントをにラップするか、「store」をpropとして「Connect(ContainerComponent)」に明示的に渡します。

これをテストする最良の方法は何ですか?

29
Detuned

私が本質的にやったことは、reduxストア(およびProvider)を持ち込み、次のようにユーティリティコンポーネントにラップすることでした。

export const CustomProvider = ({ children }) => {
  return (
    <Provider store={store}>
      {children}
    </Provider>
  );
};

次に、mountSampleComponentをテストし、それに対してテストを実行します。

it('contains <ChildComponent/> Component', () => {
  const wrapper = mount(
    <CustomProvider>
      <SampleComponent {...defaultProps} />
    </CustomProvider>
  );
  expect(wrapper.find(ChildComponent)).to.have.length(1);
});
10
Detuned

酵素のマウントはオプションのパラメーターを取ります。必要なものに必要な2つは

options.context: (Object [optional]): Context to be passed into the component

options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper次のようなオプションオブジェクトを使用してSampleComponentをマウントします。

const store = { 
  subscribe: () => {},
  dispatch: () => {},
  getState: () => ({ ... whatever state you need to pass in ... })
}
const options = {
  context: { store }, 
  childContextTypes: { store: React.PropTypes.object.isRequired } 
}

const _wrapper = mount(<SampleComponent {...defaultProps} />, options)

これで、SampleComponentは、指定したコンテキストをconnected componentに渡します。

39
zentuit

名前のエクスポートを使用して、この問題を解決できます。

次のものが必要です。

class SampleComponent extends React.Component{
...
   render(){
       <div></div>
   }
}

export default connect(mapStateToProps, mapDispatchToProps)(SampleComponent)

クラスの前にエクスポートを追加できます:

export class SampleComponent extends React.Component{

そして、このコンポーネントをreduxストアなしでインポートします。

import { SampleComponent } from 'your-path/SampleComponent';

このソリューションを使用すると、ストアをテストファイルにインポートする必要がありません。

3

オプション1)

テスト内でReact-ReduxのProviderコンポーネントでコンテナコンポーネントをラップできます。したがって、このアプローチでは、実際にストアを参照し、プロバイダーに渡し、内部でテスト対象のコンポーネントを作成します。このアプローチの利点は、テスト用のカスタムストアを実際に作成できることです。このアプローチは、コンポーネントのRedux関連部分をテストする場合に役立ちます。

オプション2)

たぶん、Redux関連の部分をテストすることを気にしないかもしれません。コンポーネントのレンダリングとローカル状態関連の動作をテストするだけの場合は、コンポーネントの接続されていないプレーンバージョンの名前付きエクスポートを追加するだけです。そして、クラスに「export」キーワードを追加するときを明確にするために、基本的には、クラスを中括弧{}を使用してまたはインポートしないで2つの方法でインポートできると言っています。例:

export class MyComponent extends React.Component{ render(){ ... }}

...

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

テストファイルの後で:

import MyComponent from 'your-path/MyComponent'; // it needs a store because you use "default export" with connect
import {MyComponent} from 'your-path/MyComponent'; // don't need store because you use "export" on top of your class.

私はそこにいるだれでも助けることを望みます。

3
darmis

redux-mock-store を使用するオプションもあります。

Redux非同期アクションクリエーターとミドルウェアをテストするための模擬ストア。モックストアは、テストのアクションログとして機能するディスパッチされたアクションの配列を作成します。

モックストアは、Reduxに必要なストアオブジェクトに必要なメソッドを提供します。オプションのミドルウェアとアプリ固有の初期状態を指定できます。

import configureStore from 'redux-mock-store'

const middlewares = []
const mockStore = configureStore(middlewares)

const initialState = {}
const store = mockStore(initialState)

const wrapper = mount(<SampleComponent store={store}/>)
2
LazerBass

デコレータ構文をよりテストしやすくするために、これを作成しました: https://www.npmjs.com/package/babel-plugin-undecorate

input:

@anyOldClassDecorator
export class AnyOldClass {
  @anyOldMethodDecorator
  method() {
    console.log('hello');   
  }
}

output:

@anyOldClassDecorator
export class AnyOldClass {
  @anyOldMethodDecorator
  method() {
    console.log('hello');   
  }
}

export class __undecorated__AnyOldClass {
  method() {
    console.log('hello');   
  }
}

うまくいけば、これが固体オプション3を提供できます!

0
SirRodge