私はパーサーを書いていて、その一部として、単一の複雑なステートメントを複数の単純なステートメントに「拡張」するExpander
クラスがあります。たとえば、これは次のように展開されます。
x = 2 + 3 * a
に:
tmp1 = 3 * a
x = 2 + tmp1
このクラスをテストする方法、具体的にはテストを配置する方法について考えています。入力構文ツリーを手動で作成できます。
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
または、文字列として記述して解析することもできます。
var input = new Parser().ParseStatement("x = 2 + 3 * a");
2番目のオプションは、はるかに単純で短く、読みやすくなっています。ただし、Parser
の依存関係も導入されます。つまり、Parser
のバグがこのテストに失敗する可能性があります。したがって、テストはExpander
の単体テストではなくなり、技術的にはParser
とExpander
の統合テストになると思います。
私の質問は、このExpander
クラスをテストするために、この種の統合テストにほとんど(または完全に)依存しても大丈夫ですか?
簡単にできれば、より多くのテスト、はるかに複雑で面白くて便利な動作を書いていることに気付くでしょう。したがって、関連するオプション
var input = new Parser().ParseStatement("x = 2 + 3 * a");
かなり有効です。別のコンポーネントに依存しています。しかし、すべては、他のコンポーネントのダースに依存しています。何かをその寿命の1インチ以内にモックする場合は、多くのモック機能とテストフィクスチャに依存していると考えられます。
開発者は時々 ユニットテストの純粋さに重点を置く 、またはモジュールなしでユニットテストとユニットテストを開発するonly 、統合、ストレス、またはその他の種類のテスト。これらのフォームはすべて有効で便利であり、Q/Aやパイプラインのさらに下の運用担当者だけでなく、すべて開発者の適切な責任です。
私が使用したアプローチの1つは、これらのより高いレベルの実行から始めて、それらから生成されたデータを使用して、テストの長い形式の最小公分母表現を構築することです。例えば。上記で生成されたinput
からデータ構造をダンプすると、次のように簡単に構成できます。
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
最低レベルでテストする一種のテスト。そうすれば、素晴らしいミックスが得られます。ごく一部の非常に基本的なプリミティブテスト(純粋な単体テスト)ですが、そのプリミティブレベルでのテストの作成に1週間も費やしていません。これにより、Parser
をヘルパーとして使用して、より多くの、わずかに少ないアトミックテストを作成するために必要な時間リソースが得られます。最終結果:より多くのテスト、より多くのカバレッジ、より多くのコーナーと他の興味深いケース、より良いコードとより高い品質保証。
もちろんOKです!
常に完全なコードパスを実行する機能/統合テストが必要です。この場合の完全なコードパスは、生成されたコードの評価を含めることを意味します。つまり、x = 2 + 3 * a
を解析すると、a = 5
で実行するとx
が17
に設定され、a = -2
で実行するとx
から-4
へ。
この下では、より小さなビットのユニットテストを実行する必要があります実際にコードのデバッグに役立つ限り。内部インターフェイスが変更されるため、コードが細かいほど、テストを変更する必要がある可能性が高くなります。このようなテストには長期的な価値はほとんどなく、メンテナンス作業が追加されます。したがって、収益が減少するポイントがあり、その前に停止する必要があります。
単体テストを使用すると、破損した特定のアイテムと、それらが破損したコードの場所を特定できます。そのため、非常に細かいテストに適しています。優れた単体テストは、デバッグ時間を短縮するのに役立ちます。
ただし、私の経験から言うと、単体テストが実際に正しい動作を検証するのに十分なほど優れていることはめったにありません。そのため、統合テストは、操作のチェーンまたはシーケンスを検証するのにも役立ちます。統合テストは、機能テストの一部です。ただし、指摘したように、統合テストは複雑であるため、コード内でテストが中断する特定の場所を見つけるのは困難です。また、チェーンのどこかで障害が発生するとテストが失敗するという点で、多少脆弱になります。ただし、プロダクションコードにはそのチェーンが含まれているため、実際のチェーンをテストすることも役立ちます。
理想的には両方を使用するのが理想的ですが、いずれにしても、自動テストを実施する方が、テストを実施しないよりも優れています。
パーサーで多くのテストを実行し、パーサーがテストに合格したら、それらの出力をファイルに保存して、パーサーをモックして他のコンポーネントをテストします。