2013-12-20 5 views
7

私はしばらくの間、ユニットテストを無視しました。私はユニットテストを書いたが、かなり貧弱だった。 私は自分自身をスクラッチに持ち込むために、 "The Unit Testingの技術"を通して読んでいます。私のようなインターフェースを持っている場合2つの機能を使用したユニットテスト

は:

public interface INotificationService 
{ 
    void AddError(string _error); 
    void AddIssue(string _issue); 

    IEnumerable<string> FetchErrors(); 
    IEnumerable<string> FetchIssues(); 
} 

をこのインタフェースの具体的な実装が含まれています

private readonly ICollection<Message> messages; 

エラーまたは問題を追加すると、それのタイプを示す列挙型を使用して新しいメッセージを作成し、それをコレクションに追加します。 FetchErrors()を呼び出す/ FetchIssues()は、その型のメッセージをコレクションから返します。

次のテストが有効だろう?:

[Test] 
    public void FetchErrors_LoggingEnabledAddErrorFetchErrors_ReturnsError() 
    { 
     notificationService = new NotificationService(); 
     notificationService.AddError("A new error"); 

     Assert.AreEqual(new []{"A new error"}, notificationService.FetchErrors()); 
    } 

私の関心は)(私が最初FetchErrorsの結果をテストし、その後、)(AddErrorを呼んでいるということです。だから私は2つの関数を呼び出しています。これは間違っていますか?

コレクションをパブリックにし、ログに記録されたエラーメッセージを含む適切なタイプのメッセージが含まれていることを直接宣言する必要がありますか?

このシナリオでのベストプラクティスは何ですか?

答えて

3

あなたのアプローチはOKです。パブリックメソッドを使用した実装のテストは、デザインで利用できる唯一のアクセスです。

メソッドを互いに独立してテストするには、ホワイトボックスのテスト用のバックドアを使用してプライベートメッセージにアクセスし、それぞれのAdd*メソッドが機能することを確認する必要があります。基礎となるCUTの実装が変更されるたびに、テストが(実行時に)中断されるため、これはまったく良い考えではありません。

反射に共通する、より良い代替はあなたにICollection<Message> messages;実装フィールドに「メンテナンスハッチのアクセスのいくつかの種類を許さテスト中の具象クラス上の非インターフェースinternal方法を公開し、その後にInternalsVisibleToのアクセスを可能にすることですユニットテストアセンブリ。

メモ - 文字列enumerableのAssert.AreEqualが参照比較を行い、(新しい配列と比較しているため)失敗することがわかります。

あなたはおそらくのようなものに変更する必要があります:

Assert.IsTrue(notificationService.FetchErrors().Contains("A new error")); 

編集

IMOは、.NETのフィールドpublicを作る限り行く必要はありません。理論的にはCUTへのアクセス手段を制限するべきインタフェース抽象化が既に存在するので、internalスコープとInternalsVisibleToを使用してUT特別なアクセスを可能にすることはかなり一般的です。ジョンスキートらは、あなたのアプローチは完全に大丈夫ですmentioned this here

#region Unit Testing side-door 
internal ICollection<Message> Messages 
{ 
    get { return _messages }; 
    // No Setter 
} 
#endregion 
+0

こんにちはStuartLC、返信いただきありがとうございます。その理由は、Roy Osheroveの本では、テスト中のクラスの新しいインスタンスをインスタンス化するようなことを提案していますが、プライベートメンバーを公開して、単一の機能のテストをより簡単にすることを提案しています。私は自分の仕事の例に関して、これをどのくらいまでとらえているのかは分かりませんでした。 –

2

持っています。ユニットテストはアレンジ - アクト - アサートの構造に従ってください。notificationService.AddError(..)への呼び出しは、単にテストのアレンジセクションの一部です。

多くの場合、テストで複数のメソッドを呼び出すことはできません。ポイントは、は単一のファクトをアサート/テストするだけです。それはあなたがすることなので、すべてが良く見えます。

+1

こんにちはトーマス、それは私には大丈夫だと思ったが、私は思った:AddError関数やFetchErrors関数にバグがあるとどうなるだろう - 両方を呼び出してテストに失敗すると、 。私は、テストが「完全」であることを考える前に、どれほど細部に進むべきかを考えています。 –

+1

まあ、厳密に言えばあなたは正しい。あなたが 'messages'フィールドに対して直接アサートしなければならないことと、' FetchErrors() 'メソッドをチェックする2回目のテストを書くことが絶対に適切であるためです。これを行うには、 'public'(醜い、悪いデザイン)にするか、' internal'にするか、 'InternalsVisib≤To'属性でテストアセンブリにアクセスさせることで、フィールドにアクセス可能にする必要があります。 –

1

他の人が指摘しているように、両方の方法を使用することはOKです。あなたはクラスの動作をテストしています。エラーが発行されたときはいつでも、それを読むことができます。

実装が複雑な場合は、方法が多すぎるため、おそらくデザインを再考する必要があります。あなたが何を記述しているのか、実際にゲッターをメソッドにする必要があるのはなぜですか?

一般に、私はテスト目的でのみ内部メソッドを公開するという考えは嫌いです。これは将来(たとえあなたが忘れた後でさえ)、そのコードを維持しようとしている人に間違ったメッセージを送ります。つまり、このアセンブリのどのクラスからでもこのコレクションに直接アクセスできます。

1

あなたのテストが有効かどうかは、投稿したコードに基づいて判断することはできません。 AddError()が何をするのか分からないので、ユニットテストが正しいかどうかを知ることはできません。

AddErrorがコレクションにエラーを追加して何か他のことを行うと、テストは良好で、FetchErrors()呼び出しはそれを検証する分離されたユニットテストを持つ必要があるため有効です。いずれの場合でも、AddErrorメソッドが何か他のことを行う場合は、テストを変更してメソッドが実行するすべてのステップを検証する必要があります。

関連する問題