データの配置が面倒すぎる場合のテスト方法


19

私はパーサーを書いており、その一部として、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、と私は、技術的の統合テストとなり推測ParserExpander

私の質問は、このExpanderクラスをテストするために、この種の統合テストにほとんど(または完全に)依存しても大丈夫ですか?


3
のバグがParser他のテストに失敗する可能性があることは、失敗が0の場合のみ習慣的にコミットする場合は問題ではありません。逆に、より多くのカバレッジがあることを意味しますParser。私が心配しているのは、のバグParserがこのテストが失敗するはずだったのに成功するかもしれないということです。結局のところ、バグを見つけるために単体テストがあります。テストは、あるべきではないのに壊れているはずです。
ジョナスケルカー14

回答:


27

簡単にできれば、はるかに複雑で、興味深く、有用な動作の、より多くのテストを書くことに気付くでしょう。したがって、関連するオプション

var input = new Parser().ParseStatement("x = 2 + 3 * a");

かなり有効です。別のコンポーネントに依存します。しかし、すべてが依存する数十の他のコンポーネントの。人生の1インチ以内に何かをモックする場合、おそらく多くのモック機能とテストフィクスチャに依存しています。

開発者は、モジュール、統合、ストレス、または他の種類のテストなし、単体テストの純度に過度に焦点を当てたり、単体テストと単体テストのみを開発したりすることがあります。これらのフォームはすべて有効で有用であり、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、ヘルパーを使用してより多くの、やや少ないアトミックテストを作成するために必要な時間リソースが得られます。最終結果:より多くのテスト、より多くのカバレッジ、より多くのコーナーと他の興味深いケース、より良いコードとより高い品質保証。


2
これは賢明です-特に、すべてが他の多くに依存しているという事実に関して。適切な単体テストでは、最小限のテストを行う必要があります。その最小可能量内にあるものはすべて、先行する単体テストでテストする必要があります。あなたが完全にパーサをテストしてみた場合、あなたが安全にテストParseStatementにパーサを使用することができると仮定することができます
ジョンストーリー

6
純度に関する主な懸念(と思う)は、単体テストで循環依存関係を記述しないようにすることです。パーサーまたはパーサーテストのいずれかがエキスパンダーを使用し、このエキスパンダーテストがパーサーの動作に依存している場合、テストするのはパーサーとエキスパンダーが一貫しているという管理しにくいリスクがありますが、あなたがしたかったのは、エキスパンダーが実際にそれがすることをすることをテストすることでし。ただし、他の方法に依存関係がない限り、この単体テストでパーサーを使用することは、単体テストで標準ライブラリを使用することと実際には変わりません。
スティーブジェソップ14

@SteveJessop良い点。独立したコンポーネントを使用することが重要です。
ジョナサンユニス14

3
パーサー自体が高価な操作(たとえば、com interopを介してExcelファイルからデータを読み取る)の場合に行ったことは、パーサーを実行し、コンソールにコードを出力するテスト生成メソッドを作成して、パーサーが返すデータ構造を再作成することです。次に、ジェネレータからの出力をより一般的な単体テストにコピーします。これにより、テストが実行されるたびにではなく、テストが作成されたときにのみパーサーが正しく機能する必要があるため、相互依存関係を減らすことができます。(Excelプロセスを作成/破棄するためにテストを数秒無駄にしないことは素晴らしいボーナスでした。)
ダンニーリー14

+1 @DanNeelyのアプローチ。同様の方法を使用して、データモデルのいくつかのシリアル化されたバージョンをテストデータとして保存します。これにより、新しいコードが引き続き古いデータで機能することを確認できます。
クリスヘイズ14

6

もちろん大丈夫です!

完全なコードパスを実行する機能/統合テストが常に必要です。この場合の完全なコードパスとは、生成されたコードの評価を含めることを意味します。それはあなたが解析がいることをテストであるx = 2 + 3 * a場合と、実行するコードを生成a = 5意志セットx17実行している場合は、とのa = -2意志セットx-4

これより下では、実際にコードのデバッグに役立つ限り、より小さなビットの単体テストを行う必要があります。テストがきめ細かくなればなるほど、内部インターフェイスが変更されるため、コードの変更でもテストを変更する必要が生じる可能性が高くなります。このようなテストには長期的な価値がほとんどなく、メンテナンス作業が追加されます。そのため、収益が減少するポイントがあり、その前に停止する必要があります。


4

単体テストを使用すると、破損する特定の項目と、コード内のどこで破損したかを特定できます。そのため、非常に細かいテストに適しています。優れた単体テストは、デバッグ時間の短縮に役立ちます。

しかし、私の経験からすると、単体テストで適切な動作を実際に検証するのに十分なことはほとんどありません。したがって、統合テストは、操作のチェーンまたはシーケンスを検証するのにも役立ちます。統合テストは、機能テストの一部を提供します。指摘したように、統合テストは複雑であるため、コード内でテストが中断する特定の場所を見つけることは困難です。また、チェーンのどこかで障害が発生するとテストが失敗するという点で、やや脆弱です。ただし、実稼働コードには引き続きそのチェーンが存在するため、実際のチェーンをテストすることは依然として役立ちます。

理想的には両方がありますが、いずれにしても、一般的に自動テストを行う方がテストを行わないよりも優れています。


0

パーサーで多くのテストを行い、パーサーがテストに合格したら、それらの出力をファイルに保存してパーサーをモックし、他のコンポーネントをテストします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.