33

それを実装するインターフェースと実装クラスがあり、このための単体テストを作成したいとします。インターフェイスまたはImplをテストする必要がありますか?

次に例を示します。

public interface HelloInterface {
    public void sayHello();
}


public class HelloInterfaceImpl implements HelloInterface {
    private PrintStream target = System.out;


    @Override
    public void sayHello() {
        target.print("Hello World");

    }

    public void setTarget(PrintStream target){
        this.target = target;
    }
}

だから、私はそれを実装するHelloInterfaceとHelloInterfaceImplを持っています。ユニットアンダーテストインターフェイスまたはImplとは何ですか?

HelloInterfaceである必要があると思います。次のJUnitテストのスケッチを検討してください。

public class HelloInterfaceTest {
    private HelloInterface hi;

    @Before
    public void setUp() {
        hi = new HelloInterfaceImpl();
    }

    @Test
    public void testDefaultBehaviourEndsNormally() {
        hi.sayHello();
        // no NullPointerException here
    }

    @Test
    public void testCheckHelloWorld() throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream target = new PrintStream(out);
        PrivilegedAccessor.setValue(hi, "target", target);
        //You can use ReflectionTestUtils in place of PrivilegedAccessor
        //really it is DI 
        //((HelloInterfaceImpl)hi).setTarget(target);
        hi.sayHello();
        String result = out.toString();
        assertEquals("Hello World", result);

    }
 }

メインラインは実際に私がコメントしたものです。

((HelloInterfaceImpl)hi).setTarget(target);

メソッドは私のパブリックインターフェイスの一部ではないので、誤って呼び出しsetTarget()たくありません。本当に呼びたいのなら、少し時間を取って考えてみてください。たとえば、私が本当にやろうとしているのは依存性注入であることを発見するのに役立ちます。それは私に新しい機会の全世界を開きます。既存の依存性注入メカニズム(たとえば、Springのメカニズム)を使用できます。実際にコードで行ったように自分でシミュレートしたり、まったく異なるアプローチをとったりすることができます。よく見てください。PrintSreamの準備はそれほど簡単ではありませんでした。代わりにモックオブジェクトを使用する必要がありますか?

編集:私は常にインターフェースに焦点を当てるべきだと思います。私の観点からsetTarget()は、implクラスの「コントラクト」の一部でもありません。依存性注入のサリーとして機能します。Implクラスのパブリックメソッドは、テストの観点からプライベートと見なす必要があると思います。ただし、実装の詳細を無視するという意味ではありません。

プライベート/保護されたメソッドを単体テストする必要がありますか?も参照してください。

EDIT-2複数の実装\複数のインターフェイスの場合、すべての実装をテストしますが、setUp()メソッドで変数を宣言するときは、必ずインターフェイスを使用します。

4

4 に答える 4

19

実装は、テストする必要のあるユニットです。もちろん、それはあなたがインスタンス化しているものであり、プログラム/ビジネスロジックを含んでいるものです。

重要なインターフェイスがあり、すべての実装が適切に準拠していることを確認したい場合は、インターフェイスに焦点を当て、インスタンスを渡す必要があるテストスイートを作成できます(実装タイプに関係なく)。

はい、おそらくPrintStreamにMockitoを使用する方が簡単です。この特定の例で行ったように、モックオブジェクトの使用を回避できるとは限りません。

于 2012-06-07T18:42:11.377 に答える
8

インターフェイスをテストします。

間違いは、System.outへの書き込みがハードワイヤードになるような方法で実装を記述したことだと思います。別のPrintStreamでオーバーライドする方法を自分に与えませんでした。セッターの代わりにコンストラクターを使用したでしょう。そのようにモックやキャストする必要はありません。

これは単純なケースです。より複雑なものには、インターフェイスのさまざまな、より複雑な実装を作成するためのファクトリがあると思います。うまくいけば、あなたはあなたが箱に入れられるような方法でそれを設計しないでしょう。

テストでインターフェースに固執することで、モックも非常に簡単になります。

public class HelloInterfaceImpl implements HelloInterface {

    private PrintStream target;

    public HelloInterfaceImpl() {
        this(System.out);
    }

    public HelloInterfaceImpl(PrintStream ps) { 
       this.target = ps;
    }

    @Override
    public void sayHello() {
        target.print("Hello World");
    }
}

テストは次のとおりです。

public class HelloInterfaceTest {

    @Test
    public void testDefaultBehaviourEndsNormally() {
        HelloInterface hi = new HelloInterfaceImpl();    
        hi.sayHello();
    }

    @Test
    public void testCheckHelloWorld() throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream target = new PrintStream(out);
        HelloInterface hi = new HelloInterfaceImpl(target);    
        hi.sayHello();
        String result = out.toString();
        assertEquals("Hello World", result);
    }
}
于 2012-06-07T18:41:11.957 に答える
7

私は常に実装をテストします-1つのクラスは複数のインターフェイスを実装でき、1つのインターフェイスは複数のクラスで実装できます-それぞれをテストでカバーする必要があります。

単体テストでsetterを呼び出すための要件(実装へのインターフェースのキャスト):

((HelloInterfaceImpl)hi).setTarget(target);

実際に実装をテストすることを意味します。これは契約の一部ではありませんが、実装を機能させるための重要な部分であり、適切にテストする必要があります。

JDKの例を見てみましょう。インターフェイスListと2つの実装があります:ArrayListLinkedList。さらに、インターフェイスをLinkedList実装しDequeます。インターフェイスのテストを作成する場合、List何をカバーしますか?配列またはリンクリスト?さらに、の場合LinkedList、どのインターフェースをテストすることを選択しますか?DequeまたはList?ご覧のとおり、実装をテストする場合、そのような問題はありません。

私にとって、個人的には、単体テストでの実装へのインターフェースのキャストは、何かがうまくいかないことの明らかな兆候です;)

于 2012-06-07T19:59:21.813 に答える
1

私はそれが実装とそれがインターフェースの契約を超えて何をするかに依存すると言うでしょう。多くの実装は、インターフェースで提供される機能のみを実装しますが、他の実装では、インターフェースはクラス機能のごく一部にすぎません。複数のインターフェースを実装する場合があります。

最終的には、実装をテストしています。

あなたが定義したような単純なケースでは、私は他の1つと6つのうちの6つを言います。テストケースをインターフェースまたは実装に書き込みます。実装を十分にテストする限り、結果は同じです。

別の例を見てみましょう。実際のリーダーとライターを装飾することで、通信チャネルの統計を収集するクラスがあります。私のクラスはこれらのインターフェースを実装する可能性がありますが、統計も収集します。これはどちらのコントラクトとも関係ありません。確かに、これらのインターフェイスに基づいてテストを作成することはできますが、このクラスを完全にテストすることはできません。

于 2012-06-07T19:50:36.007 に答える