Karma と Webpack を使用した React および Flux アプリケーションのテスト

リアクトフラックス
Rescale では、 冗談 React および Flux アプリケーションをテストするための Facebook によるフレームワーク。 Jest は、次のような API を備えた、簡単に始められるテスト フレームワークです。 ジャスミンただし、CommonJS モジュールの自動モックが組み込まれています。ただし、オープンソース コミュニティの多くの開発者と同様に、 注意、重要なテスト スイートを実行すると、Jest が耐えられないほど遅くなる可能性があることがわかりました。 約 60 のテスト ケースからなるテスト スイートの場合、完了までに 10 秒以上かかります。 この記事では、私たちがどのようにしてそれをやめたかについて説明します 冗談 賛成して カルマ, Webpack, ジャスミン。 新しいセットアップで同じテスト スイートを実行すると、最初の Webpack バンドルの後、PhantomJS での実行にかかる時間はわずか 200 ミリ秒弱です。
私たちのテスト設定は、この記事で説明されているものに基づいています。 Karma と Webpack を使用した ReactJS コンポーネントのテスト。 Karma と Webpack に慣れていない場合は、こちらをお読みください。 要約すると、このセットアップは karma-webpack を使用して、すべてのテストを単一のファイルにバンドルし、Karma がブラウザーにロードして実行します。 これに必要な npm パッケージは次のとおりです。

  • カルマ
  • カルマクリ
  • カルマジャスミン
  • Karma-phantomjs-ランチャー
  • カルマウェブパック
  • ジャスミン
  • ウェブパック
  • core-js (ES5 シム用)
  • babel-loader (ES6 から ES5 へのトランスパイル用)

Karma 設定ファイル karma.conf.js は次のようになります。

module.exports = function(config) { config.set({ ブラウザ: ['PhantomJS'], ファイル: [ { パターン: 'tests.webpack.js', 監視: false }, ], フレームワーク: ['jasmine'] 、プリプロセッサ: { 'tests.webpack.js': ['webpack'], }、レポーター: ['dots']、singleRun: true、webpack: { module: { ローダー: [ { test: /\.jsx?$ /, exclude: /node_modules/, ローダー: 'babel-loader' }, ], }, watch: true, }, webpackServer: { noInfo: true, }, }); };

また、テスト スイート Webpack バンドルのエントリ ポイント、tests.webpack.js は次のようになります。

// Function.prototype.bind、Object.prototype.keys などの ES5 シム require('core-js/es5'); // ./src/js をアプリケーション コードのディレクトリに置き換え、 // ファイル名 regexp がテスト ファイルと一致することを確認してください。 var context = require.context('./src/js', true, /-test\.js$/); context.keys().forEach(コンテキスト);

これは ./src/js/components/__tests__/MemberList-test.js にあるサンプル テストで、コンポーネント ./src/js/components/MemberList.jsx をテストします。

var React = require('react'); var TestUtils = require('react/lib/ReactTestUtils'); var MemberList = require('../MemberList.jsx'); description('MemberList', () => { it('renders', () => { var element = TestUtils.renderIntoDocument(); Expect(element).toBeTruthy(); }); });

React コンポーネントをテストする場合はこれで十分ですが、Flux アプリケーション、特に Flux ストアをテストするには、もう少しセットアップが必要です。

Flux ストアのテスト

Flux ストアの問題は、ストアが通常、状態を持つシングルトンであることです。 状態を持つシングルトン これらの状態はテスト間で持続するため、テストするには最悪の項目の XNUMX つになる可能性があります。 アプリケーションの多くの部分がテスト スイート全体でストアと対話すると、ストアの状態を予測することが難しくなります。 各テスト ケースの前にストアの状態をリセットするメソッドを実装すればよいのではないかと思うかもしれませんが、すべてのストアに対してこれを行うのは保守性が悪夢であり、エラーが発生する可能性が非常に高くなります。
Jest は、すべてのテスト ケースを独自の環境で実行することで、この問題を解決します。 つまり、テスト ケースでモジュールが必要な場合、インポートされるモジュールは新しいインスタンスになります。 これはまさにストアをテストするときに必要な動作です。
Webpack を使用すると、まさにそれが可能になります。各テスト ケースの前に require キャッシュをクリアして、require の呼び出しによってモジュールの新しいインスタンスが読み込まれるようにします。 最適化として、React などのサードパーティ モジュールを必須キャッシュから削除することは望ましくありません。 そうすることにより、テスト スイートの速度が大幅に低下するため、いずれにせよ、これらのサードパーティ モジュールはいずれも状態を持つシングルトンであってはなりません。 次のように、tests.webpack.js ファイルの各テストの前にキャッシュ無効化を追加できます。

// Webpack の require コンテキストを作成して、 // プロジェクトのモジュールを動的に要求できるようにします。 このコンテキストではテスト ファイルを除外します。 var projectContext = require.context('./src/js', true, /^((?!__tests__).)*.jsx?$/); // Webpack がモジュールを追跡するために使用するモジュール ID を抽出します。 var projectModuleIds = projectContext.keys().map(module => String(projectContext.resolve(module))); beforeEach(() => { // 各テスト ケースの前に、require キャッシュからモジュールを削除します。 projectModuleIds.forEach(id => delete require.cache[id]); });

必須キャッシュを無効にする方法はこれですべてです。 モジュールは各テスト ケース内にキャッシュされますが、その間にはキャッシュされません。 ストア、アクション、ディスパッチャーはすべてテスト間で分離されます。

アクション/ストア境界のテスト

を使用しております 還流 これは私たちのプロジェクトの XNUMX つに対する Flux 実装であり、アクションはストア用のパブリック API とほぼ同じです。 ただし、アクションのトリガーは非同期操作であるため、テストで非同期を制御する方法が必要です。 これは、ネイティブの setTimeout 関数と setInterval 関数を模擬して手動で進める機能を提供するテスト フレームワークやライブラリを使用すれば簡単に行うことができます。 Jasmine を使用してアクション/ストア境界をテストする例を次に示します。

description('MemberStore', () => { var MemberActions; var MemberStore; beforeEach(() => { jasmine. Clock().install(); // 組み込みタイマーのモックアウト MemberActions = require('../ ../actions/MemberActions'); MemberStore = require('../MemberStore'); }); afterEach(() => { jasmine. Clock().uninstall(); }); it(' の場合にメンバーを保存しますselected', () => { MemberAction.memberReceived({ name: 'Baz Fu' }); jasmine. Clock().tick(); // 時計を次のティックまで進める Expect(MemberStore.getAll().first) ().get('名前')).toBe('バズフー'); }); });

モジュールの依存関係をモックする

モックしたい別のモジュールに依存関係があるモジュールをテストしたいとします。 一例として、次のような http モジュールに依存する API モジュールをテストしたいことが考えられます。 アクシオス。 ここで Jest が輝くのは、 jest.setMock(moduleName, moduleExports) API または自動モック機能を使用してモックを簡単に指定できるからです。
Jest の外部でこれを実現する XNUMX つの方法は、次のとおりです。 再配線 とその Webpack バージョン ウェブパックを再配線する。 Rewire を使用して、モジュール内のプライベート変数を変更できます。 たとえば、api.js モジュール内で axios モジュールをモックアウトしたい場合は、次のように記述できます。

// api.js var axios = require('axios'); var API = { doSomething() { axios.get('https://someurl/'); }、}; module.exports = API; // 一部のテストでは var API = rewire('../api'); API.__set__('axios'、mockAxios);

別のオプションは、Webpack の require キャッシュをもう一度操作することです。

require('axios'); // モジュールがロードされ、キャッシュされていることを確認します require.cache[require.resolve('axios')].exports = mockAxios;

このオーバーリワイヤの利点は、リワイヤしたモジュール内だけでなく、require('axios') へのすべての呼び出しがモックされることです。
注: http リクエスト ライブラリのモックはおそらく悪い例です。 そのためには、次のようなものを使用する必要があります ジャスミン-アジャックス 代わりに、しかし、あなたはアイデアを理解しています。

閉鎖

Jest 上で Karma と Webpack を使用した React および Flux アプリケーション テスト スイートの実行では、約 50 倍の速度向上が見られました。 ただし、Jest と比較してセットアップにはもう少し知識と労力が必要で、モジュールの依存関係をモックするのはそれほど簡単ではありません。 Jest はセットアップが簡単なので本当に便利ですが、非常に遅いことが私たちにとって問題であり、それが修正されるかどうかはわかりません。 その間、現在のセットアップでは効果的に TDD を行うことができます。

類似の投稿