2008-09-02 9 views
28

インターフェイスIFooがあり、それを実装するいくつかのクラスがある場合、これらのクラスをインターフェイスに対してすべてテストするにはどのような方法が良いですか?NUnit - 特定のインターフェイスを実装するすべてのクラスをテストする方法

私はテストコードの重複を減らしたいと思いますが、単体テストの原則には依然として「真実」を残しています。

あなたはベストプラクティスをどのように考えますか?私はNUnitを使用していますが、どのユニットテストフレームワークの例も有効でしょう。

答えて

13

クラスにいずれかのインターフェイスが実装されている場合は、そのインターフェイスにメソッドを実装する必要があります。これらのクラスをテストするには、クラスごとに単体テストクラスを作成する必要があります。

代わりにスマートなルートを使用できます。あなたの目標がで、コードとテストコードの重複を避ける場合は、の代わりに、を反復するコードを処理する抽象クラスを作成するとよいでしょう。

など。

public interface IFoo { 

    public void CommonCode(); 

    public void SpecificCode(); 

} 

あなたは抽象クラスを作成することがあります:あなたは、次のインタフェース持っ

public abstract class AbstractFoo : IFoo { 

    public void CommonCode() { 
      SpecificCode(); 
    } 

    public abstract void SpecificCode(); 

} 

テストは簡単です。

[TextFixture] 
public void TestClass { 

    private class TestFoo : AbstractFoo { 
     boolean hasCalledSpecificCode = false; 
     public void SpecificCode() { 
      hasCalledSpecificCode = true; 
     } 
    } 

    [Test] 
    public void testCommonCallsSpecificCode() { 
     TestFoo fooFighter = new TestFoo(); 
     fooFighter.CommonCode(); 
     Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); 
    } 
} 

...またはそれはあなたの空想に合った場合、テストクラスは抽象クラス自体を拡張してみましょう:テストクラスのいずれかで内部クラスとして抽象クラスを実装します。

[TestFixture] 
public void TestClass : AbstractFoo { 

    boolean hasCalledSpecificCode; 
    public void specificCode() { 
     hasCalledSpecificCode = true; 
    } 

    [Test] 
    public void testCommonCallsSpecificCode() { 
     AbstractFoo fooFighter = this; 
     hasCalledSpecificCode = false; 
     fooFighter.CommonCode(); 
     Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); 
    }   

} 

抽象クラスを持つことにより、インタフェースが暗示する共通コードを扱うことで、よりクリーンなコード設計が可能になります。

これはあなたにとって理にかなっていると思います。サイドノートとして


、これはTemplate Method patternと呼ばれる一般的なデザインパターンです。上記の例では、テンプレートメソッドはCommonCodeメソッドであり、SpecificCodeはスタブまたはフックと呼ばれます。アイデアは、誰もがシーンの背後にあるものを知る必要なしに行動を拡張できるということです。

多くのフレームワークは、この動作パターンに依存しています。 ASP.NETページでフックを実装する必要がある場合や、Loadイベントで呼び出される生成されたPage_Loadメソッドなどのユーザーコントロールの場合、テンプレートメソッドはバックグラウンドでフックを呼び出します。これのもっと多くの例があります。基本的に、 "load"、 "init"、または "render"という言葉を使用して実装する必要があるものは、テンプレートメソッドによって呼び出されます。

+1

@ Spoike:+1ですが、小さなコードはあなたのコードがC#ではないということです。 – user7116

+1

@sixlettervariables:ええ、私は仮想キーワードとそれ以外のものを追加することを忘れていました。私はこの記事を書いたのと同じ時間にやらなければならなかったすべてのJavaプログラミングに責任がある; – Spoike

+0

ニースの答え!現在、NUnitは汎用テストクラスをサポートしており、TestFixture属性を使用して、テストの実行時に使用する特定のタイプを指定することができます。私はテストする方法について[ブログの投稿](http://softwareonastring.com/2015/03/22/testing-every-implementer-of-an-interface-with-the-ame-tests-using-nunit)を書いたこれらの機能を示すインタフェースのすべての実装者。 –

0

私はNUnitを使用しませんが、C++インターフェイスをテストしました。私は最初に一般的なものが動作することを確認するための基本的な実装であるTestFooクラスをテストします。次に、各インターフェイスに固有のものをテストするだけです。

3

これはベストプラクティスではないと思います。

単純な真実は、インタフェースはメソッドが実装されている契約に過ぎないということです。それはではなく、のどちらかで契約しています。)メソッドをどのように実装すべきかb)そのメソッドが正確に何をしているのか(戻り値の型だけを保証する)、私が手に入れた2つの理由は、この種のテスト。

あなたが本当にあなたのメソッドの実装の制御になりたい場合は、オプションの持っている:

抽象クラスのメソッドとしてそれを実装する
  • を、その継承。あなたはまだそれを具体的なクラスに継承する必要がありますが、明示的にオーバーライドされない限り、そのメソッドは正しいことを行います。インタフェース

例を参照する拡張法などの方法を実装する.NET 3.5/C#3.0で

  • public static ReturnType MethodName (this IMyinterface myImplementation, SomeObject someParameter) 
    { 
        //method body goes here 
    } 
    

    任意の実装が適切にその拡張メソッドを参照すると、正確にその拡張を放出しますメソッドを使用して、一度だけテストする必要があります。

  • 11

    彼が言うとき、私はそれはどちらかの契約ではありません

    Jon Limjapに反対。)メソッドが実装され、Bすべきか。)そのメソッドが正確にやるべきものを(それが唯一の保証あなたがこの種のテストを望んでいるあなたの動機になるのは、私が収集する2つの理由です。

    返品タイプに契約の多くの部分が指定されていない可能性があります。言語に依存しない例:

    public interface List { 
    
        // adds o and returns the list 
        public List add(Object o); 
    
        // removed the first occurrence of o and returns the list 
        public List remove(Object o); 
    
    } 
    

    のLinkedList、ArrayListに、CircularlyLinkedList、および他のすべてのあなたのユニットテストは、リスト自体が返されることはなく、また、彼らは適切に変更されたことだけでなく、テストする必要があります。

    契約書にはearlier questionがありました。これは、これらのテストをDRYする1つの方法で正しい方向を指すのに役立ちます。

    あなたが契約のオーバーヘッドをしたくない場合は、私がSpoike推奨何の線に沿って、テストリグをお勧めします。

    abstract class BaseListTest { 
    
        abstract public List newListInstance(); 
    
        public void testAddToList() { 
        // do some adding tests 
        } 
    
        public void testRemoveFromList() { 
        // do some removing tests 
        } 
    
    } 
    
    class ArrayListTest < BaseListTest { 
        List newListInstance() { new ArrayList(); } 
    
        public void arrayListSpecificTest1() { 
        // test something about ArrayLists beyond the List requirements 
        } 
    } 
    
    1

    インターフェイスまたは基本クラスの契約をテストする場合、私はテストを聞かせすることを好みますフレームワークは自動的にすべての実装者を見つけることに注意します。これにより、テスト中のインターフェイスに集中し、多くの手動実装を行うことなく、すべての実装がテストされることを合理的に確かめることができます。 xUnit.netについては

    • 、私は(xUnit.net拡張子がタイプリゾルバ機能の上にだけ薄いラッパーなので、それは他のフレームワークでの使用に適合させることができる特定のタイプのすべての実装を検索するためのType Resolverライブラリを作成)。
    • MbUnitには、パラメータにUsingImplementationsの属性を持つCombinatorialTestを使用できます。
    • 他のフレームワークでは、前述の基本クラスパターンSpoikeが便利です。

    インターフェイスの基本をテストする以外にも、個々の実装がそれぞれの特定の要件を満たしていることをテストする必要があります。

    1

    @Emperor XLII

    私がMbUnitの中に、組み合わせテストの音が好きで、私はNUnitの抽象ベースクラスインターフェース・テスト技術を試してみた、そしてそれは作業を行いますが、別々のテストを持っている必要があるだろうクラスが実装するすべてのインタフェース用のフィクスチャ(C#では複数の継承がないため、内部クラスを使用できますが、かなりクールです)。 実際にはこれはうまくいくかもしれません。それはインターフェイスによって実装クラスのテストをグループ化するためです。しかし、フレームワークがよりスマートになることができればよいでしょう。私がクラスをインターフェイスの「公式な」テストクラスとしてマークするために属性を使うことができれば、フレームワークはそのインターフェイスを実装するすべてのクラスについてテスト中のアセンブリを検索し、それらのテストを実行します。

    これはクールだ。

    1

    [TestFixture]クラスの階層はどうですか?共通のテストコードをベーステストクラスに入れ、それを子テストクラスに継承してください。

    関連する問題