web-dev-qa-db-ja.com

他のクラスを順次呼び出すクラスのユニットテスト

こんにちは、このようなクラスがあります

class MyClass {

  private ExternalClass1 ex1;
  private ExternalClass2 ex2;
  private ExternalClass3 ex3

public String doSomething(String arg1){ 
 val1=ex1.invoke(arg1); 
 val2=ex2.call(val1); 
 result=ex3.doit(val2);
 return result;
}    

}

このメソッドの単体テスト

@Test
void doSometing(){
   ExternalClass1 ex1=mock(ExternalClass1);
   ExternalClass2 ex2=mock(ExternalClass2);
   ExternalClass3 ex3=mock(ExternalClass2);

when(ex1.invoke(arg1)).thenReturn(val1);
when(ex2.call(val1)).thenReturn(val2);
when(ex3.doit(val2)).thenReturn(result);

Myclass myClass=new MyClass(ex1,ex2,ex3);

assertEquals(result,myClass.doSomething(arg1))
}

このメソッドのテストコードは、コード自体の単純な繰り返しのようですが、より複雑です。他のクラスを制御する役割を持つこの種のクラスのテストは何か価値がありますか?

ありがとう

5
Belin

何か価値がありますか?

はい-相互作用をテストしています。

呼び出されている3つのメソッドの機能をユニットテスト済みの場合は、正しく実行されることが保証されています。この種のテストはState Test;です。メソッドを呼び出し、正しい結果が得られることを確認してください。

しかし、ここではInteraction Test;を実行します。一部のメソッドが正しく呼び出されたことをテストするもの(これらのメソッドがデータに対して何を行うかわからない場合があるため、結果の値を一貫してテストすることはできません)。

相互作用テストを実行するには、モックを変更して各メソッドにインクリメンターを追加し、インクリメントされた変数が期待どおりであることをアサートします。

インクリメンターの実装方法は、クラスの作成方法とモックの仕方によって異なります。

簡単なseudoの例として:

@Test
void doSomething()
{
    ExternalClass1 ex1=mock(ExternalClass1);
    ExternalClass2 ex2=mock(ExternalClass2);
    ExternalClass3 ex3=mock(ExternalClass2);

    int ex1Count = 0;
    int ex2Count = 0;
    int ex3Count = 0;

    when(ex1.invoke(arg1)).do(ex1Count++).thenReturn(val1);
    when(ex2.call(val1)).do(ex2Count++).thenReturn(val2);
    when(ex3.doit(val2)).do(ex3Count++).thenReturn(result);

    Myclass myClass = new MyClass(ex1,ex2,ex3);

    myClass.doSomething(arg1);

    assertEquals(1, ex1Count);
    assertEquals(1, ex2Count);
    assertEquals(1, ex3Count);

    // assertEquals(2, ex1Count); // If for example ex3 utilises ex1 once (and you know that it should)...
}
6
Steve Padmore

それがもたらす唯一の価値は、コードカバレッジです。つまり、doSometing()メソッドにカバレッジがあることを意味します。それは価値がありますか?たぶん管理報告書に書かれているかもしれませんが、動作をテスト/検証する限り、それは価値がありません。

DoSometing()メソッドはラッパーまたはファサードです。他のメソッドの呼び出し以外のロジックは含まれていないため、テストは必要ありません。 ExternalClass1、ExternalClass2、およびExternalClass3に対する他のテストは、実際の機能をカバーします。

2
Jon Raynor

機能テストでは、doSomethingを呼び出して、それによって妥当な順序でテストされる必要がある場合があります。あなたのユニットテストはおそらくそれが呼び出すすべての部分をすでにチェックしています。

nitテストでdoSomethingをテストできるのは、そのエラー処理です。 3つの呼び出しのいずれかが特定の入力で失敗する可能性があり、そのような無効な入力で例外が発生する(または発生しない)ことについて特定の期待がある場合、テストする価値があります。 doSomethingのクライアントはそれに依存している可能性が高く、その実装の詳細に依存したくありません。

1
9000

いいえ!現状ではありません。

問題の特定のユニットに大きく依存して、はるかに深い深さ、またははるかに少ないが必要です。現状では、テストはdoSomething()ex3.doit()の戻り値を返すことをonly検証します。

そう ...

気にした場合方法doSomething()が機能します...必ずしもそうとは限りませんatypicalan adapterfacade 、または ラッパーのすべての種類 をテストする方法。しかし、この実装をロックダウンする必要があり、テストはひどく不足します。 ラッパーがプロセス全体を正しく管理することは実際には保証されません。

そのレベルの保証を実現するには、期待どおりに各内部メソッドが期待どおりに呼び出されることを検証する必要もあります。

ただし、これはまれな必要性であり、私の経験では。私は本当に内部実装の詳細を検証する必要がありましたonce... API要求ロガーの作成:ロガーが基礎となるHTTP接続を非常に特殊な方法で管理するようにする必要がありました。そして、私は完全な統合テストを実行する自由がありませんでした...テストデータを使用して(自分のコントロールの外で)本番APIを非難するためです。

その場合、実際に本番環境で使用するHTTPクラスの完全な偽物を作成しました。ロガーとそれが管理していたリソースの間のすべての小さなやり取りを記録しました。そして、私はそれらの相互作用の詳細について主張するたくさんのテストをしました。

ただし、ほとんどの場合、テストではdoSomething()の動作を気にする必要はありません。彼らは効果結果が正しいことを気にする必要があります。

したがって、doSomething()が実際にどのように機能するかを気にしない場合...統合テストを記述します。

私の意見では、「統合テスト」は依然として「単体テスト」です。単位はたまたま「大きい」。

1
svidgen