web-dev-qa-db-ja.com

React-Redux接続コンポーネントの単体テスト方法

ユニットテストには、Mocha、Chai、Karma、Sinon、Webpackを使用しています。

このリンクに従って、React-Reduxコードのテスト環境を構成しました。

テストの実装方法+ React Karma、Babel、およびWebpackで)のコードカバレッジ

アクションとリデューサーのJavaScriptコードを正常にテストできますが、コンポーネントのテストに関しては常にエラーが発生します。

import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils'; //I like using the Test Utils, but you can just use the DOM API instead.
import chai from 'chai';
// import sinon from 'sinon';
import spies from 'chai-spies';

chai.use(spies);

let should = chai.should()
  , expect = chai.expect;

import { PhoneVerification } from '../PhoneVerification';

let fakeStore = {
      'isFetching': false,
      'usernameSettings': {
        'errors': {},
        'username': 'sahil',
        'isEditable': false
      },
      'emailSettings': {
        'email': '[email protected]',
        'isEmailVerified': false,
        'isEditable': false
      },
      'passwordSettings': {
        'errors': {},
        'password': 'showsomestarz',
        'isEditable': false
      },
      'phoneSettings': {
        'isEditable': false,
        'errors': {},
        'otp': null,
        'isOTPSent': false,
        'isOTPReSent': false,
        'isShowMissedCallNumber': false,
        'isShowMissedCallVerificationLink': false,
        'missedCallNumber': null,
        'timeLeftToVerify': null,
        '_verifiedNumber': null,
        'timers': [],
        'phone': '',
        'isPhoneVerified': false
      }
}

function setup () {
    console.log(PhoneVerification);
    // PhoneVerification.componentDidMount = chai.spy();
    let output = TestUtils.renderIntoDocument(<PhoneVerification {...fakeStore}/>);
    return {
        output
    }
}

describe('PhoneVerificationComponent', () => {
    it('should render properly', (done) => {
        const { output } = setup();
        expect(PhoneVerification.prototype.componentDidMount).to.have.been.called;
        done();
    })
});

この次のエラーは、上記のコードで発生します。

FAILED TESTS:
  PhoneVerificationComponent
    ✖ should render properly
      Chrome 48.0.2564 (Mac OS X 10.11.3)
    Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

シノンスパイからチャイスパイへの切り替えを試みました。

React-Redux Connected Components(Smart Components)の単体テストはどのようにすればよいですか?

47
Ayushya

これを行うためのよりきれいな方法は、プレーンコンポーネントと、接続にラップされたコンポーネントの両方をエクスポートすることです。名前付きエクスポートはコンポーネントになり、デフォルトはラップされたコンポーネントになります。

export class Sample extends Component {

    render() {
        let { verification } = this.props;
        return (
            <h3>This is my awesome component.</h3>
        );
    }

}

const select = (state) => {
    return {
        verification: state.verification
    }
}

export default connect(select)(Sample);

この方法で、通常どおりアプリにインポートできますが、テストに関しては、import { Sample } from 'component'

46
Ashwin van Dijk

接続されたコンポーネントをテストできます。そうする必要があると思います。最初に接続されていないコンポーネントをテストすることもできますが、接続されたコンポーネントもテストしないと完全なテストカバレッジが得られないことをお勧めします。

以下は、私がReduxとEnzymeで行っていることのテストされていない抽出物です。中心的なアイデアは、プロバイダーを使用して、テスト中の状態をテスト中の接続済みコンポーネントに接続することです。

import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import SongForm from '../SongForm'; // import the CONNECTED component

// Use the same middlewares you use with Redux's applyMiddleware
const mockStore = configureMockStore([ /* middlewares */ ]);
// Setup the entire state, not just the part Redux passes to the connected component.
const mockStoreInitialized = mockStore({ 
    songs: { 
        songsList: {
            songs: {
                songTags: { /* ... */ } 
            }
        }
    }
}); 

const nullFcn1 = () => null;
const nullFcn2 = () => null;
const nullFcn3 = () => null;

const wrapper = mount( // enzyme
        <Provider store={store}>
          <SongForm
            screen="add"
            disabled={false}
            handleFormSubmit={nullFcn1}
            handleModifySong={nullFcn2}
            handleDeleteSong={nullFcn3}
          />
        </Provider>
      );

const formPropsFromReduxForm = wrapper.find(SongForm).props(); // enzyme
expect(
        formPropsFromReduxForm
      ).to.be.deep.equal({
        screen: 'add',
        songTags: initialSongTags,
        disabled: false,
        handleFormSubmit: nullFcn1,
        handleModifySong: nullFcn2,
        handleDeleteSong: nullFcn3,
      });

===== ../SongForm.js

import React from 'react';
import { connect } from 'react-redux';

const SongForm = (/* object */ props) /* ReactNode */ => {
    /* ... */
    return (
        <form onSubmit={handleSubmit(handleFormSubmit)}>
            ....
        </form>

};

const mapStateToProps = (/* object */ state) /* object */ => ({
    songTags: state.songs.songTags
});
const mapDispatchToProps = () /* object..function */ => ({ /* ... */ });

export default connect(mapStateToProps, mapDispatchToProps)(SongForm)

純粋なReduxでストアを作成することもできます。 redux-mock-storeは、テスト用の軽量バージョンです。

AirbnbのEnzymeの代わりにreact-addons-test-utilsを使用することもできます。

私は、airbnbのchai-enzymeを使用して、React対応のオプションを期待しています。この例では必要ありませんでした。

14
JohnSz

受け入れられた答えの問題は、テストするためだけに不必要に何かをエクスポートしていることです。私の意見では、クラスをテストするためだけにクラスをエクスポートするのは得策ではありません。

接続されたコンポーネント以外をエクスポートする必要のない、すてきなソリューションを次に示します。

Jestを使用している場合は、connectメソッドをモックして3つのことを返すことができます。

  1. mapStateToProps
  2. mapDispatchToProps
  3. ReactComponent

そうするのはとても簡単です。インラインモックまたはグローバルモックの2つの方法があります。

1。インラインモックの使用

テストの記述機能の前に次のスニペットを追加します。

_jest.mock('react-redux', () => {
  return {
    connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
      mapStateToProps,
      mapDispatchToProps,
      ReactComponent
    }),
    Provider: ({ children }) => children
  }
})_

2。ファイルモックの使用

  1. ルート(package.jsonがある場所)にファイル___mocks__/react-redux.js_を作成します
  2. ファイルに次のスニペットを追加します。
_module.exports = {
  connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
    mapStateToProps,
    mapDispatchToProps,
    ReactComponent,
  }),
  Provider: ({children}) => children
};_

モックの後、_Container.mapStateToProps_、_Container.mapDispatchToProps_および_Container.ReactComponent_を使用して上記3つすべてにアクセスできるようになります。

コンテナは、次のようにするだけでインポートできます。

_import Container from '<path>/<fileName>.container.js'_

それが役に立てば幸い。

ファイルモックを使用する場合は注意してください。模擬ファイルはすべてのテストケースでグローバルに使用されます(テストケースの前にjest.unmock('react-redux'))を実行しない限り)。

編集:私は上記を詳細に説明する詳細なブログを書きました:

http://rahulgaba.com/front-end/2018/10/19/unit-testing-redux-containers-the-better-way-using-jest.html

7
Rahul Gaba

ストアまたは何か(PhoneVerification-component.js)を認識せずに、コンポーネント自体を含む2つのファイルを作成してみてください。次に、アプリケーションで使用し、connect関数を介してストアにサブスクライブされた最初のコンポーネントのみを返す2番目のコンポーネント(PhoneVerification.js)

import PhoneVerificationComponent from './PhoneVerification-component.js'
import {connect} from 'react-redux'
...
export default connect(mapStateToProps, mapDispatchToProps)(PhoneVerificationComponent)

次に、テストでPhoneVerification-component.jsを要求し、必要な模擬プロップを提供することにより、「ダム」コンポーネントをテストできます。テスト済みのテストのポイントはありません(接続デコレータ、mapStateToProps、mapDispatchToPropsなど)。

1
george.cz