システムをテストするには、副作用を分離します

副作用を取り除くことは、テスト可能なコードを構築する最良の方法の1つです。

2人の男性戦闘機のボクシングの試合の写真。彼らの顔はフレームの外にあります。左側の戦闘機は、右側の戦闘機に左フックを派遣しました。右側の戦闘機は、ソビエト連邦の小さなシンボルの付いた赤いショートパンツを着ています。

Nockは、ネットワーク要求をスタブ化するのに役立つJavaScriptで記述された有名なライブラリです。 HTTPサーバーが使用できない場合でも実行できるように、テストの静的応答を返します。

しかし、それは匂いでもあります。

結果としてデータソースとテスト対象システムの間の結合は、コードのリファクタリングと保守性に影響を与える可能性のあるコストです。

その理由は次のとおりです。

投稿のリストを返すサーバーと、そのサーバーからの応答を使用して投稿タイトルのリストを作成する関数があるとします。関数のテストでは、Nockを使用してサーバーからの応答をスタブ化します。

「投稿のリストを作成」というキャプションが付いた左側のブロックを示す図。 1つの矢印は、「応答を投稿する」というキャプションを持つブロックを指します。他の矢印は、キャプションが「フェッチ」のブロックを指します。キャプションが「フェッチ」のブロックには、キャプションが「HTTPサーバー」のブロックの方向を指す矢印が1つあります。最後の矢印は、ラベル「Nocked」で中断されます。

コードにはまともなカバレッジがあります。ただし、それにはいくつかの問題があります。

応答のコンテンツタイプを変更する場合、コードの動作が同じ場合でも、テストを変更する必要があります。

Nockがスタブ化するURL、ヘッダー、またはパラメーターに変更を加えた場合も同様です。システムの動作が同じ場合でも、テストを変更する必要があります。

機能「投稿のリストを作成」は、テスト中のシステム(SUT)です。 HTTP呼び出しからのデータはデータソースです。

データソースがSUTにプラグイン可能な一般的なインターフェイスを持つようにコードを設計できます。その場合、多くのセットアップを必要とせずにロジックを実行できます。

キャプション「投稿タイトルのリスト」のある左側のブロックを示す図。一方の矢印はキャプション「In-Memory Data Source」のあるブロックを指しています。もう一方の矢印はキャプション「HTTP Server Data」のあるブロックを指していますソース。

テスト環境では、「インメモリデータソース」を挿入できます。本番環境では、「HTTPサーバーデータソース」を使用できます。

前のJSFiddleの「一般的なインターフェース」は「投稿のタイトルを見つける」メソッドです。インターフェースの作成方法に関係なく、すべての呼び出し元を制御できます。したがって、変更は簡単です。マーティン・ファウラーはそれを「非公開インターフェース」と呼んでいます。

一方、クラス属性がpost-titleからarticle-titleに変更されるなど、サーバーが発行済みインターフェイスの契約に違反する場合、データソースの実装を変更するだけです。どこでも変更する必要はありません。

テストし、早期にフィードバックすることが重要なのは、データではなく、動作に対するテストです。したがって、ロジックの変更に必要な労力を削減するコードを設計することが重要です。この場合、ロジックは、データソースからHTMLの順序付けられていないリストへの入力の変換です。

新しい設計では、テスト中のシステムからデータソースを切り離しました。したがって、Nockを削除できます。

また、新しい設計により、コピー/貼り付けなしでシステムに新しいルールを追加するために必要な作業が削減されます。

それでも、「HTTP Server Data Source」には、プライベート関数「query posts title from html」内に未テストのロジックがいくつかあります。

それをテストするために、同じパターンを繰り返すことができます。副作用をプッシュし、「get request」メカニズムを「HTTP Server Data Source」にプラグイン可能にします。この方法では、Nockを必要とせずにコードをテストできます。

「投稿タイトルのリスト」が「インメモリデータソース」で機能することを確認するためのテストがすでにある場合、データソースを単独でテストして、正しい結果が返されることを確認できます。

ロジックから副作用を完全に排除しました。この場合、実際の「get request」関数は副作用です。これで、Nockを使用してそれをカバーできます。

ただし、「get request」内のロジックは簡単であり、Nockにはかなりのコストがあるため、副作用を含めてアプリケーション全体を実行できる少数の統合テストを用意することは理にかなっています。 Nockを使用してライブサーバーへの接続を回避し、HTTPリクエストを使用して、すべての要素が一致したときにアプリケーションが適切な応答を返すかどうかを確認できます。

Nockは、HTTP層で接続をスタブ化し、静的な応答を提供するのに役立ちます。ただし、控えめに使用してください。スタブするテストごとに、大幅な結合と変更のコストが増加します。

控えめに使用しない場合、NockはNock Hellを作成できます。

解決したい問題は、バグの数と変更のコストを削減することです。動作に変更を加えずにコードの構造を変更しても、テストは中断されません。もしそうなら、あなたは有用なテストを書くことに失敗しました。

目標は、関心のあるロジックのテストカバレッジの品質を改善し、早期のフィードバックを達成することです。コードをリファクタリングする能力に影響を与えることなく、すべて。

副作用を特定し、Nockなどのツールの使用をアプリケーションの境界に制限します。

これにより、変更を加えても、物を壊すことなく十分な自信が得られます。

戦いに加わり、副作用を押し出してから…ノックアウトしてください。

読んでくれてありがとう。フィードバックがある場合は、Twitter、Facebook、またはGithubで私に連絡してください。

この投稿に対する洞察に富んだフィードバックをしてくれたEduardo SlompoとGuilherme J. Tramontinaに感謝します。