2016-04-03 7 views
4

私の質問は、多くのインターフェイスを実装するクラスをテストすることです。たとえば、私はこのクラスを持っています:クラスが多くのインターフェイスを実装していることをどのようにテストできますか?

public class ServiceControllerImpl extends ServiceController implements IDataChanged, IEventChanged { 

} 

これで2つのテスト方法があります。最初は具体的なクラスで直接テストしています。つまり、オブジェクトタイプはインターフェイスではなく具象クラスです。

public class ServiceControllerImplTest { 
    ServiceControllerImpl instance; 
    @Before 
    public void setUp() { 
     instance = new ServiceControllerImpl(); 
     // you can bring this instance anywhere 
    } 
} 

第2の方法は、インターフェイスでのテストのみです。このオブジェクトを実装するすべてのインターフェイスにこのオブジェクトを型キャストする必要があります。

public class ServiceControllerImplTest { 
    ServiceController instance;  // use interface here 
    IDataChanged dataChangeListener; 

    @Before 
    public void setUp() { 
     instance = new ServiceControllerImpl(); 
     dataChangeListener = (IDataChanged) instance; 
     // instance and dataChangeListener "look like" two different object. 
    } 
} 

多分将来的に我々はそれが他のオブジェクトに実装インターフェイスを変更することができますので、具体的なクラスを使用すると、将来的には失敗のテストにつながる可能性があるので、私は第二の溶液を好みます。私はこの問題のベストプラクティスを知らない。

感謝:)

+2

ええ、インターフェイスの種類をテストするインターフェイスをテストします。だから、2番目のソリューションは基本的には行く方法です。 – JayC667

+1

私は上記のコメントが答えを与えると確信していますが、あなたの質問は少し明確にする必要があります。 'dataChangeListener =(IDataChanged)dataChangeListener;を書いたとき、あなたは' dataChangeListener =(IDataChanged)instance;を意味しました。 –

+0

@DaveSchweisguthはい。私は編集しました。本当にありがとう。 – hqt

答えて

1

私は第二の溶液を好む現実には、おそらく将来的に我々はそれが他のオブジェクトに実装インターフェイスを変更するので、使用して強制することができますので、将来のフェイルテストにつながる可能性があります。

通常、アサーションが真であるか偽であるかをテストするので、とにかく失敗したテストにつながると思います。問題は次のとおりです。そのテストはIDataChangedに適用されますか、またはこれらのアサーションはServiceControllerImplにのみ適用されますか?異なるアサーション - アサーションのみServiceControllerImplに適用された場合は、代わりにServiceControllerImplIDataChangedを使用する場合は、別のIDataChangedオブジェクトを使用するときにテストを編集する必要がありますので、

は、それは、問題ではありません。別のオブジェクトを使用すると、テストは失敗します。

セットアップユニットのテスト方法は、あなた自身に答えを与えます。単体テストは通常​​、あるクラスを単独でテストします。これは、あなたが環境をモックすることを意味します。しかし、環境を嘲笑するということは、テストするクラスの依存関係を知っていることを意味します。これは実装の詳細です。したがって、あなたのテストはインターフェイスだけではなく、実装ベースで書かれています。

インタフェースのように抽象APIのみをテストするテストを書くことは可能です。しかし、これは通常あなたのテストも抽象的であることを意味します。例えば。

public abstract class SetTest { 

    @Test 
    public void addAlreadyExistentObject(){ 
     Set<String> setUnderTest = createSetUnderTest(); 
     Assert.assertTrue(setUnderTest.isEmpty()); 

     boolean setChanged = setUnderTest.add("Hello"); 
     Assert.assertTrue(setChanged); 

     setChanged = setUnderTest.add("Hello"); 
     Assert.assertFalse(setChanged); 

     Assert.assertEquals(setUnderTest.size(), 1); 

    } 

    protected abstract Set<String> createSetUnderTest(); 

} 

これらの抽象的なテストを拡張して、具体的なクラスのAPIをテストすることができます。例えば。

public class HashSetTest extends SetTest { 

    @Override 
    protected Set<String> createSetUnderTest() { 
     return new HashSet<String>(); 
    } 
} 

この場合、実装を置き換えることができ、テストは緑色のままでなければなりません。

ただし、テスト中のオブジェクトを置き換えることが実際には意味をなさない抽象APIの別の例です。 Runnableのすべてのテストの作成はどうですか?

public class RunnableTest { 

    @Test 
    public void run(){ 
     Runnable runnable = ...; 

     // What to test here? 
     // run is invoked without throwing any runtime exceptions? 
     runnable.run(); 

    } 
} 

あなたはそれはあなたが簡単にテスト対象のオブジェクトを置き換えることができるような方法でテストを書くためにいくつかのケースでは意味がありません見ることができるように。

Set apiのようなapiが具体的な状態処理を定義する場合、これをテストする抽象的なテストを書くことができます。

1

JayC667はすでに正しく、それはこれらのタイプによって定義されたメソッドのテストでそのスーパータイプ(複数可)を介してクラスを参照するのが最善だと答えました。しかし、私は少しキャストを避けるために、あなたがやった方法を変更したい:

public class ServiceControllerImplTest { 
    ServiceController controller; 
    IDataChanged dataChangeListener; 

    @Before 
    public void setUp() { 
     instance = new ServiceControllerImpl(); 
     controller = instance; 
     dataChangeListener = instance; 
    } 
} 
関連する問題