TDDで常にユニットテストを記述していますか?


8

私は長い間、TDDスタイルのコードを設計および開発してきました。TDDについて私を悩ませているのは、ビジネスロジックや興味深い動作を含まないコードのテストを作成することです。TDDはテスト以上の設計アクティビティであることは知っていますが、これらのシナリオでテストを記述することは役に立たないと感じることがあります。

たとえば、「ユーザーがチェックボタンをクリックすると、ファイルの有効性をチェックする」という簡単なシナリオがあります。このシナリオでは、通常、以下のようなプレゼンター/コントローラークラスのテストの作成を開始します。

@Test
public void when_user_clicks_check_it_should_check_selected_file_validity(){
    MediaService service =mock(MediaService);
    View view =mock(View);

    when(view.getSelectedFile).thenReturns("c:\\Dir\\file.avi");

    MediaController controller =new MediaController(service,view);
    controller.check();

    verify(service).check("c:\\Dir\\file.avi");
}

ご覧のとおり、動作を検証するための設計上の決定や興味深いコードはありません。MediaServiceに渡されたビューから値をテストしています。私はいつも書いていますが、この種のテストは好きではありません。これらの状況についてどうしますか?あなたはいつもテストを書いていますか?

更新:

苦情の後にテスト名とコードを変更しました。一部のユーザーは、このような些細なケースのテストを記述して、将来誰かが興味深い動作を追加する可能性があると述べました。しかし、「今日のコード、明日のデザイン」についてはどうでしょう。?私を含む誰かが将来さらに興味深いコードを追加した場合、そのテストを作成できます。ささいなことでなぜ今やらなければならないのですか?


1
「一部のユーザーは、このような些細なケースのテストを作成する必要があると言ったので、将来誰かが興味深い動作を追加するかもしれません。しかし、「今日のコード、明日のデザイン」についてはどうでしょうか。? "見事な反論!+1
maple_shaft

回答:


5

コードカバレッジの100%を目指していません。そして、私は通常、明らかにビジネスロジックや数行を超えるコードを含まないメソッドのテストを記述しません。しかし、私はまだそれほど複雑ではないように見えるメソッドの単体テスト(TDDを使用)を記述しています。これは主に、数か月または数年後にそのコードに戻ったときにすでに単体テストを行い、それをより複雑にしたいためです。すべてをゼロから構築するよりも、既存のテストを拡張する方が常に簡単です。ヌーファルが言ったように、それは主観的です。メソッドが少し複雑であるか、より複雑になる可能性がある場合は、テストを作成することをお勧めします。


2

これは、作成するテストの数に不快感を与えるTDDの今日の2番目の質問です。

「機能させる場合にのみテストしてください。」

質問のテストを理解しているかどうかは本当にわかりません。

Controller.Check()が、ビューから選択されたファイル値として引数を使用してサービス(依存関係)に委任していることを確認していますか?はいの場合、これは良いテストです。これにより、実際のサービス実装なしでコントローラーをテストできます。(相互作用ベースのマイクロテスト)。

更新:テストの対象が明確になったので、コードを移動していくつかの名前を変更し、「選択したファイルチェックをメディアサービスにテストを委任します」と表示されるようにします。-これはコントローラの有効な仕様です。

public class TestMediaController

@Test
public void DelegatesSelectedFileCheckToMediaService(){
    string selectedMediaFileInView = "c:\\Dir\\file.avi";

    when(_view.getSelectedFile).thenReturns(selectedMediaFileInView);

    new MediaController(_service, _view).check();

    verify(_service).check(selectedMediaFileInView);
}

はいサービスクラスに委任します。

しかし、質問:このテストを記述して、ビューからサービスクラスに渡されるパラメーターのみを検証する価値はありますか?

@はい-check()が正しく配線されていることをテストします。Service.Check()が呼び出しを受信すると機能する別のテストがあるはずです。このテストは今日はささいなことのように見えますが、時間が経つにつれ、誰かがこのメソッドにコードを追加し(たとえば、ある条件が返された場合の保護句)、既存の動作を壊す可能性があります。このテストはそれを防ぐものです。
岐阜

わかりましたが、「今日のコード、明日のデザイン」についてはどうでしょうか。?誰かまたは私が将来さらに興味深いコードを追加する場合、そのためのテストを作成できます。ささいなことでなぜ今やらなければならないのですか?

1
@mcaaltuntas:TDDの要点は、現在の仕様をテストすることです。そのため、将来のコード変更が現在の仕様を満たさなくなった場合は、それについて調べます。今すぐに仕様を実装するのがどれほど簡単だと思っていても、誰かが将来この特定の要件に注意を払わずにコードを変更する可能性があります。 。ここでテストを作成しない場合は、TDDを実行していません。仕様全体をテストしたくない場合、それはあなたの呼び出しですが、TDDをその部分に適用していません。
スティーブジェソップ

1

そのようなテストは作成しません(または、少なくともそのように名前を付けません)。代わりに、への呼び出しを必要とする機能のテストを作成しますcheck()。そのため、そのチェックまたは同等のアクションが実行されない場合、高レベルの機能は機能しません。コードでメソッドを呼び出す必要があるのはなぜcheck()ですか?

一般的に、私はテストを実装の詳細から切り離しておくようにしています。そのため、少なくともテストの名前はオブジェクトによって提供される外部機能についてのみ語ります。オブジェクトやメソッドなどの実装の詳細は、テスト名には記載されていません。

これにより、リファクタリングが容易になり(実装を変更するときにテストを変更する必要がなくなるはずです)、テストが古いかどうかを簡単に確認できます(指定された機能はもう必要ありません)。 。また、低レベルのボイラープレート(ゲッターやセッターなど)は、高レベルの機能で必要な場合にのみ追加/保持されるため、不要なコードやデッドコードに気づきやすくなります。


「ユーザーがチェックをクリックすると、選択したファイルのプロパティをチェックする必要がある」という名前を思い付くことができます。しかし、問題は名前を付けることではありません。「明白な」コードのテストを書くかどうか

テスト名を変更しましたが、疑問が残ります。

チェックが失敗した場合、システムは何をすべきですか?例外か何かを投げますか?私はおそらく2つのテストを作成します。ファイルが有効な場合と無効な場合の結果です。ファイルが無効になる可能性のある方法がたくさんある場合は、MediaServiceに対して直接これらのテストを記述します。
Esko Luontola

はいProbaby私も2つのテストを記述しますが、Controllerクラスに対しては記述しません。MediaServiceクラスに対して記述します(あなたが言ったように)。したがって、実際には、テストはサービスクラスがビューからのパラメーターで呼び出されたことを確認するだけです。このテストを作成して、ビューからサービスクラスに渡された引数のみを検証する価値はあると思いますか?

「このテストを作成して、ビューからサービスクラスに渡された引数のみを検証する価値はあると思いますか?」場合によります。チェックが機能することを確認するエンドツーエンドのテストがある場合(つまり、ファイルが無効な場合にエラーメッセージまたは何かが表示されます)、コントローラーのユニットテストでは、メソッドを確認するだけで十分な場合があります。呼び出されます。エンドツーエンドのテストよりもリファクタリング中の保護が速くなります。
Esko Luontola

0

これは主観的です。私は常に TDD を行うわけでありませんが、行う場合、テストが包括的であるかどうかの指標としてコードカバレッジを維持しようとします。時々、私は怠惰になって、私には「明白」に見える部分を単にスキップします。Red、Green、Refactorサイクルに違反し、必要以上のコードを作成することがありますが、時間が経つにつれ、自分が慣れていないリズムに慣れてしまいました。


0

上記のようなクラスと、テストを書くためのより単純なオリジナルとの相互作用。相互作用は時間の経過とともにより複雑になる可能性があるため、適切な基盤を整えることは良いことです。


0

誰かまたは私が将来さらに興味深いコードを追加する場合、そのためのテストを作成できます。ささいなケースでなぜ今やらなければならないのですか?

あなたは将来、誰かがこのUI要素が存在することと、それがバックエンドで何を呼び出すかを知っていると仮定します。

現在のプロジェクトでは、同じコードベース内に6つの異なるチームの30人を超える開発者がいます。些細なUIの追加は常に霧の中に消えます。誰もが戻ってこのテストケースを後で追加することはありません。それがそこにあることを誰も覚えておらず、それが壊れると、「なぜテストを作成しなかったのですか?とても簡単だったでしょう!」


0

いつものように...

場合によります

ストーリーがわからないため、TDDのコンテキストでこの特定のテストの有用性を確認することは困難です。

ストーリーが[メディアユーザー]の場合[メディアの有効性を確認できるようにしたい] [ファイルが利用できない場合に通知する]

次に、シナリオ[メディアのチェックボタン]が指定された場合[ユーザーがボタンをクリックしたとき]次に[ファイルの有効性がチェックされます]

理にかなっています。取るに足らないことですが、理にかなっています。

包括的なストーリーがこれよりも大きい場合、シナリオの定義が狭すぎる可能性があります。

覚えておいてください:

TDD!=ユニットテスト

TDD は機能をテストします。それが機能である場合、それを検証するためのテストに値します。

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