バックエンドのテストは簡単です。選択した言語を選択し、それをお気に入りのフレームワークと組み合わせて、いくつかのテストを作成し、「実行」をクリックします。あなたのコンソールは言う 'わーい!できます!' 継続的インテグレーションサービスは、プッシュするたびにテストを実行します。人生は素晴らしいものです。
確かに、テスト駆動開発(TDD)は最初は奇妙ですが、予測可能な環境、複数のテストランナー、フレームワークに組み込まれたテストツール、継続的インテグレーションのサポートにより、作業が楽になります。 5年前、私はテストがこれまでに経験したすべての問題の解決策だと思っていました。
その後、バックボーンが大きくなりました。
私たちは皆に切り替えました フロントエンド MVC。テスト可能なバックエンドは、栄光のデータベースサーバーになりました。最も複雑なコードがブラウザに移動しました。そして、私たちのアプリは実際にはテストできなくなりました。
これは、フロントエンドコードをテストして UIコンポーネント ちょっと難しいです。
モデルが正常に動作することを確認するだけであれば、それほど悪くはありません。または、関数を呼び出すと正しい値が変更されます。 Reactユニットテストのために必要なことは次のとおりです。
それでおしまい。私たちのコードはユニットテストされています。
昔はそうだった ランニング フロントエンドテストは難しい部分でした。すべてのフレームワークには独自のアイデアがあり、ほとんどの場合、テストを実行するたびに手動で更新するブラウザーウィンドウが表示されます。もちろん、あなたはいつも忘れるでしょう。少なくとも、私はそうしました。
2012年、VojtaJinaは カルマランナー (当時はTestacularと呼ばれていました)。 Karmaを使用すると、フロントエンドテストはツールチェーンの完全な市民になります。 Reactテストはターミナルまたは継続的インテグレーションサーバーで実行され、ファイルを変更すると自動的に再実行されます。また、複数のブラウザーで同時にコードをテストすることもできます。
これ以上何を望むことができますか?さて、 実際に フロントエンドコードをテストします。
単体テストは優れています。アルゴリズムが毎回正しいことを行うかどうかを確認したり、入力検証ロジック、データ変換、その他の分離された操作を確認したりするのに最適な方法です。ユニットテストはファンダメンタルズに最適です。
ただし、フロントエンドコードは、データの操作に関するものではありません。ユーザーイベントと適切なビューを適切なタイミングでレンダリングすることが重要です。フロントエンドはユーザーに関するものです。
これが私たちができることをしたいことです:
これを行ってきた10年間、Reactを突っ込み始めるまで、ユーザーインタラクションをテストしてレンダリングを表示する適切な方法を見つけられませんでした。
Reactは、これらの目標を達成するための最も簡単な方法です。部分的には、それが私たちにアプリを設計することを強制する方法のために テスト可能なパターン 、部分的には、素晴らしいReactテストユーティリティがあるためです。
これまでReactを使用したことがない場合は、私の本をチェックしてください。 React + d3.js 。視覚化を対象としていますが、 「素晴らしい軽量イントロ」 反応する。
Reactは、すべてを「コンポーネント」として構築することを強制します。 Reactコンポーネントは、ウィジェット、またはいくつかのロジックを備えたHTMLのチャンクと考えることができます。それらは、オブジェクトであることを除いて、関数型プログラミングの多くの最良の原則に従います。
AWSソリューションアーキテクト認定の準備方法
たとえば、同じパラメーターセットが与えられた場合、Reactコンポーネントは常に同じ出力をレンダリングします。何度レンダリングしても、誰がレンダリングしても、出力をどこに配置しても。いつも同じ。その結果、Reactコンポーネントをテストするために複雑なスキャフォールディングを実行する必要がありません。それらはプロパティのみを気にし、グローバル変数と構成オブジェクトの追跡は必要ありません。
私たちは、状態を回避することによってこれを大部分達成します。あなたはこれを呼ぶでしょう 参照の透明性 関数型プログラミングで。 Reactにはこれに特別な名前はないと思いますが、公式ドキュメントでは、状態の使用をできるだけ避けることを推奨しています。
ユーザーインタラクションのテストに関しては、Reactは関数コールバックにバインドされたイベントでカバーしています。テストスパイを設定し、クリックイベントが適切な関数を呼び出すことを確認するのは簡単です。また、Reactコンポーネントはそれ自体をレンダリングするため、クリックイベントをトリガーして、HTMLの変更を確認するだけです。これは、Reactコンポーネントがそれ自体のみを気にするために機能します。クリック ここに 物事を変えない そこ 。明確に定義された関数呼び出しだけで、イベントハンドラーのネストを処理する必要はありません。
ああ、Reactは魔法なので、DOMについて心配する必要はありません。 Reactはいわゆる 仮想DOM コンポーネントをJavaScript変数にレンダリングします。そして、仮想DOMへの参照は、実際にReactコンポーネントをテストするために必要なすべてです。
とても甘いです。
TestUtils
Reactには一連の組み込みTestUtils
が付属しています。 Jestというおすすめのテストランナーもいますが、私は好きではありません。その理由を少し説明します。まず、TestUtils
。
require('react/addons').addons.TestUtils
のようなことでそれらを取得します。これは、ユーザーインタラクションをテストして出力を確認するためのエントリポイントです。
React TestUtils
Reactコンポーネントをページに挿入する代わりに、そのDOMを変数に配置してレンダリングしましょう。たとえば、Reactコンポーネントをレンダリングするには、次のようにします。
var component = TestUtils.renderIntoDocument( );
次に、TestUtils
を使用できますすべての子がレンダリングされたかどうかを確認します。このようなもの:
var h1 = TestUtils.findRenderedDOMComponentWithTag( component, 'h1' );
findRenderedDOMComponentWithTag
子を調べて、探しているコンポーネントを見つけて、それを返します。戻り値はReactコンポーネントのように動作します。
その後、getDOMNode()
を使用できます生のDOM要素にアクセスし、その値をテストします。 h1
を確認するにはコンポーネントのタグは言う 'タイトル' 、これを書きます:
expect(h1.getDOMNode().textContent) .toEqual('A title');
まとめると、完全なテストは次のようになります。
it('renders an h1', function () { var component = TestUtils.renderIntoDocument( ); var h1 = TestUtils.findRenderedDOMComponentWithTag( component, 'h1' ); expect(h1.getDOMNode().textContent) .toEqual('A title'); });
すばらしい点は、TestUtilsでユーザーイベントもトリガーできることです。クリックイベントの場合、次のように記述します。
var node = component .findRenderedDOMComponentWithTag('button') .getDOMNode(); TestUtils.Simulate.click(node);
これはクリックをシミュレートし、潜在的なリスナーをトリガーします。リスナーは、出力、状態、またはその両方を変更するコンポーネントメソッドである必要があります。これらのリスナーは、必要に応じて親コンポーネントの関数を呼び出すことができます。
すべてのケースをテストするのは簡単です。変更された状態はcomponent.state
にあり、通常のDOM関数で出力にアクセスでき、スパイで関数呼び出しを行うことができます。
Reactの公式ドキュメントでは、https://facebook.github.io/jest/をテストランナーおよびReactテストフレームワークとして使用することを推奨しています。 JestはJasmine上に構築されており、同じ構文を使用します。 Jasmineから得られるすべてのものに加えて、Jestはテストしているコンポーネント以外のすべてをモックします。これは理論的には素晴らしいですが、私はそれが面倒だと思います。まだ実装していないもの、またはコードベースの別の部分からのものは、undefined
です。これは多くの場合問題ありませんが、静かに失敗するバグにつながる可能性があります。
たとえば、クリックイベントのテストに問題がありました。私が何を試しても、リスナーを呼び出さないだけです。それから私は関数がジェストによって嘲笑されたことに気づきました、そしてそれは私にこれを決して教えませんでした。
しかし、Jestの最悪の犯罪は、これまで、新しい変更を自動的にテストするための監視モードがなかったことでした。一度実行してテスト結果を取得することができ、それだけです。 (私は仕事中にバックグラウンドでテストを実行するのが好きです。そうでなければ、テストを実行するのを忘れます。)現在、これはもはや問題ではありません。
ああ、Jestは複数のブラウザでのReactテストの実行をサポートしていません。これは以前ほど問題ではありませんが、まれな機会に重要な機能だと思います。 特異なバグ Chromeの特定のバージョンでのみ発生します…
編集者のメモ:この記事が最初に書かれて以来、Jestは大幅に改善されました。あなたは私たちのより最近のチュートリアルを読むことができます、 酵素とジェストを使用したReactユニットテスト 、そしてJestテストが今日のタスクに対応しているかどうかを自分で判断します。
とにかく、理論的には、優れたフロントエンドReactテストがどのように機能するかを見てきました。短い例でそれを実行に移しましょう。
Reactとで作成された散布図コンポーネントを使用して乱数を生成するさまざまな方法を視覚化します。 d3.js 。コードとそのデモも Githubで 。
Karmaをテストランナーとして、Mochaをテストフレームワークとして、Webpackをモジュールローダーとして使用します。
ソースファイルは/src
になりますディレクトリを作成し、テストを/src/__tests__
に配置しますディレクトリ。 src
内に、主要コンポーネントごとに1つずつ、それぞれ独自のテストファイルを持つ複数のディレクトリを配置できるという考え方です。このようにソースコードとテストファイルをバンドルすると、さまざまなプロジェクトでReactコンポーネントを簡単に再利用できます。
ディレクトリ構造が整ったら、次のような依存関係をインストールできます。
$ npm install --save-dev react d3 webpack babel-loader karma karma-cli karma-mocha karma-webpack expect
何かがインストールに失敗した場合は、インストールのその部分を再実行してみてください。 NPMは、再実行時に消える方法で失敗することがあります。
私たちのpackage.json
完了すると、ファイルは次のようになります。
// package.json { 'name': 'react-testing-example', 'description': 'A sample project to investigate testing options with ReactJS', 'scripts': { 'test': 'karma start' }, // ... 'homepage': 'https://github.com/Swizec/react-testing-example', 'devDependencies': { 'babel-core': '^5.2.17', 'babel-loader': '^5.0.0', 'd3': '^3.5.5', 'expect': '^1.6.0', 'jsx-loader': '^0.13.2', 'karma': '^0.12.31', 'karma-chrome-launcher': '^0.1.10', 'karma-cli': '0.0.4', 'karma-mocha': '^0.1.10', 'karma-sourcemap-loader': '^0.3.4', 'karma-webpack': '^1.5.1', 'mocha': '^2.2.4', 'react': '^0.13.3', 'react-hot-loader': '^1.2.7', 'react-tools': '^0.13.3', 'webpack': '^1.9.4', 'webpack-dev-server': '^1.8.2' } }
いくつかの構成が完了すると、npm test
のいずれかを使用してテストを実行できるようになります。またはkarma start
。
構成にはあまり意味がありません。 Webpackがコードの検索方法を知っていること、およびKarmaがテストの実行方法を知っていることを確認する必要があります。
2行のJavaScriptを./tests.webpack.js
に配置しますKarmaとWebpackが一緒にプレイするのに役立つファイル:
// tests.webpack.js var context = require.context('./src', true, /-test.jsx?$/); context.keys().forEach(context);
これにより、Webpackは-test
で何でも考慮するようになります。テストスイートの一部となるサフィックス。
Karmaの構成には、もう少し作業が必要です。
// karma.conf.js var webpack = require('webpack'); module.exports = function (config) { config.set({ browsers: ['Chrome'], singleRun: true, frameworks: ['mocha'], files: [ 'tests.webpack.js' ], preprocessors: { 'tests.webpack.js': ['webpack'] }, reporters: ['dots'], webpack: { module: { loaders: [ {test: /.jsx?$/, exclude: /node_modules/, loader: 'babel-loader'} ] }, watch: true }, webpackServer: { noInfo: true } }); };
これらの行のほとんどは、デフォルトのKarma構成からのものです。 browsers
を使用しましたテストはChromeで実行する必要があると言うと、frameworks
使用しているテストフレームワークを指定し、singleRun
デフォルトでテストを1回だけ実行するようにします。 karma start --no-single-run
を使用すると、カルマをバックグラウンドで実行し続けることができます。
これらの3つは明らかです。 Webpackのものはもっと面白いです。
Webpackはコードの依存関係ツリーを処理するため、files
ですべてのファイルを指定する必要はありません。アレイ。必要なのはtests.webpack.js
だけで、必要なすべてのファイルが必要です。
webpack
を使用しますWebpackに何をすべきかを指示する設定。通常の環境では、この部分はwebpack.config.js
になります。ファイル。
また、Webpackに使用するように指示します babel-loader
JavaScript用です。これは私たちにからのすべての素晴らしい新機能を与えます ECMAScript2015 そして ReactのJSX 。
webpackServer
で構成では、デバッグ情報を出力しないようにWebpackに指示します。それは私たちのテスト出力を台無しにするだけです。
テストスイートを実行すると、残りは簡単です。ランダムな座標の配列を受け入れ、多数の点を持つ要素を作成するコンポーネントを作成する必要があります。
Reactテストのベストプラクティスに従う-つまり標準的なTDDプラクティス-最初にテストを記述し、次に実際のReactコンポーネントを記述します。 src/__tests__/
のバニラテストファイルから始めましょう。
// ScatterPlot-test.jsx var React = require('react/addons'), TestUtils = React.addons.TestUtils, expect = require('expect'), ScatterPlot = require('../ScatterPlot.jsx'); var d3 = require('d3'); describe('ScatterPlot', function () { var normal = d3.random.normal(1, 1), mockData = d3.range(5).map(function () { return {x: normal(), y: normal()}; }); });
まず、React、そのTestUtils、d3.js、expect
が必要です。ライブラリ、およびテストしているコード。次に、describe
を使用して新しいテストスイートを作成し、ランダムデータを作成します。
最初のテストでは、ScatterPlot
を確認しましょう。タイトルをレンダリングします。私たちのテストはdescribe
の内部に入りますブロック:
// ScatterPlot-test.jsx it('renders an h1', function () { var scatterplot = TestUtils.renderIntoDocument( ); var h1 = TestUtils.findRenderedDOMComponentWithTag( scatterplot, 'h1' ); expect(h1.getDOMNode().textContent).toEqual('This is a random scatterplot'); });
ほとんどのテストは同じパターンに従います。
前に見たように、renderIntoDocument
コンポーネントをレンダリングしますfindRenderedDOMComponentWithTag
テストしている特定の部分を見つけ、getDOMNode
生のDOMアクセスを提供します。
最初、私たちのテストは失敗します。合格するには、タイトルタグをレンダリングするコンポーネントを作成する必要があります。
var React = require('react/addons'); var d3 = require('d3'); var ScatterPlot = React.createClass({ render: function () { return ( {this.props.data.map(function (pos, i) { var key = 'circle-'+i; return ( ); }.bind(this))}; ); }
このコードはthis.props.data
を通過します配列し、各データポイントの要素を追加します。シンプル。
Reactとd3.jsを組み合わせてデータ視覚化コンポーネントを作成する方法について詳しく知りたい場合は、私の本をチェックするもう1つの大きな理由です。 React + d3.js 。
Reactを使用してテスト可能なフロントエンドコンポーネントを作成するために知っておく必要があるのはこれだけです。 Reactコンポーネントをテストするその他のコードを確認するには、前述のように、GithubでReactテストのサンプルコードベースを確認してください。
私たちはそれを学びました:
この記事が気に入ったら、 Twitterでフォローしてください 以下にコメントを残してください。読んでくれてありがとう、そして幸せなReactテスト!
関連: コンポーネントを最適化してReactのパフォーマンスを向上させる方法コンポーネントテストはユニットテストに似ていますが、より高いレベルであり、ユニット間のある程度の統合が含まれます。場合によっては、これはアプリケーション全体のコンテキストで実行する必要があります。
ベストプラクティスを使用して設計された場合のReactコンポーネントのモジュール式で反復可能な性質により、アプリケーションの残りの部分を必要とせずに、単体テストのようにコンポーネントテストを実行できます。テストフレームワーク(例:ジャスミン)とテストランナー(例:カルマ)のみが必要です。
単体テストは、機能ごとに、可能な限り最高かつ最も分離されたレベルでテストします。入力のセットが与えられた場合、関数が正しい出力を返すかどうかがわかります。