2016-07-25 2 views
1

私はコーディングの初心者です。ユニットテストC#モックインターフェイスは別のインターフェイスの中に構成されています

私はA、クラスを持っている:

public class A 
{ 
    public InterfaceB _b; 

    public A() 
    { 
     _b = new B(); 
    } 

    public string functionA() 
    { 
     if(String.IsNullOrEmpty(_b.GetId())) 
      return String.Empty; 
     else if(String.IsNullOrEmpty(_b.GetKey())) 
      return String.Empty; 
     else 
      return _b.GetToken(); 
    } 
} 

public interface InterfaceB 
{  
    string GetId(); 
    string GetKey(); 
    string GetToken(); 
} 

私はinterfaceBのすべての3つの方法の中に浸透することができますfunctionAをテストしたいです。私の単体テストでは、クラスAのインスタンスを作成し、それを呼び出すと、クラスBの動作を設定できません。

それはデータベースにぶつかり続けますが、他のテストケースでは必要です。

ロジック全体をテストできるように、これを完全に模擬するにはどうすればよいですか?

+0

、その後、あなたが直接そのコンストラクタに嘲笑オブジェクト 'InterfaceB'を渡すか、それを設定することができますあなたがDIを使用している場合は、そのテストのためだけであっても、注射のために必要です。 – starlight54

答えて

4

Aと模擬インターフェイスInterfaceBをテストするには、のインスタンスを作成しないようにAと記述する必要があります。代わりに、そのコンストラクタを通じてInterfaceBのインスタンスを受け取ります。

あなたは何度も何度もこのパターンが表示されます:

public A() 
{ 
    private readonly InterfaceB _b; 

    public A(InterfaceB b) 
    { 
     _b = b; 
    } 

    public string functionA() 
    { 
     if(String.IsNullOrEmpty(_b.GetId())) return String.Empty; 
     else if(String.IsNullOrEmpty(_b.GetKey())) return String.Empty; 
     else return _b.GetToken(); 
    } 
} 

これは、依存性注入と呼ばれています。つまり、クラスを作成するクラスではなく、クラスの依存関係をクラスに「注入」するということです。このようにコンストラクタに注入すると、コンストラクタインジェクションと呼ばれることもありますが、一般的には「依存性注入」です。あなたが尋ねているようなインターフェースを模擬できることは、私たちがそれを使う理由の1つです。

いくつかの重要な詳細:InterfaceBは、コンストラクタに渡されるため

  • Aには何も今までに実際の実装が何であるかを「知りません」。それは何でもかまいません。その結果、Aは決して具体的な実装に結びついていません。 (だからあなたは "模擬"できるInterfaceB
  • フィールド_breadonlyです。これは厳密には必要ではありませんが、_bはコンストラクタからしか設定できず、再び変更されることはありません。それはAを受け取ってそれを使用することを強調します。このクラスは決して_bが何であるか決して制御しません。 が何であれAがその値が何であるかを決定します。

    :今、あなたはあなたが

    public class MockedInterfaceB : InterfaceB 
    { 
        private string _id; 
        private string _key; 
        private string _token; 
    
        public MockedInterfaceB(string id, string key, string token); 
        { 
         _id = id; 
         _key = key; 
         _token = token; 
        } 
        public string GetId() {return _id}; 
        public string GetKey() {return _key}; 
        public string GetToken() {return _token}; 
    } 
    

    のように、正確に何をしたいんInterfaceBの嘲笑の実装を作成することができます。そして、あなたのユニットテストでは、あなたがその実装を使用することができますユニットテストを書いている

var testA = new A(new MockedInterfaceB("myid","mykey","mytoken")); 

Moqのようなツールを使用して、これらのモックをより簡単に作成することもできます。

依存性注入が聞こえると、それはしばしばCastle Windsor、Autofac、Unityなどの依存性注入コンテナのコンテキストにあります。これらは依存性注入作業を手助けするための便利なツールです。彼らは学ぶ価値がある。しかし、依存関係の注入は、クラスAに依存性(InterfaceB)を "注入"する例のように、あなたがクラスをどのように記述するかというところです。

+0

恐ろしいスコット! Id、key、TokenはsqlのテーブルBに存在します。私は、空でゼロ以外のゼロのIDのようないくつかのシナリオをシミュレートしたいと思います。私は依存関係を注入しているので、ソースコードに触れたくないので、私はCIの一部でそれをチェックしたいと思うので、InterfaceBをパラメータとして余分なコンストラクタを書きたいとは思わない。 Moqを使用する場合、2つの別個のモックIAとIBを作成して、BがA?で構成されているときに期待値を設定し、別のモークを別のモンクに注入できますか?もしそうなら、これを達成する方法は? – user2791094

+0

そのような後方互換性が必要な場合は、コンストラクタパラメータをオプションにして、デフォルトを指定します。必要に応じて、テストのためにクラスを手動で作成することができます。 1つのオブジェクトを作成し、1つのテストで目的のオブジェクトを挿入し、もう一方のテストにはもう1つを注入します。テストオブジェクトを作成するメソッドを記述することができます。 –

0

実際には、Typemockを使用すると、ソースコードを変更せずにメソッドをテストすることができます。 Aのインスタンスを作成する前に、将来のインスタンスとしてBをモックすることができます。その後、Bのメソッド動作を変更することができます。例えば

:あなたは、パラメータとして `InterfaceB`を取る` A`にコンストラクタが必要になります

[TestMethod,Isolated] 
public void AllBMethodReturnStrings_WillReturnSuccess() 
{ 
    // Arrange 
    // Mocking future B's instance 
    var fakeIB = Isolate.Fake.NextInstance<B>(); 
    var realA = new A(); 

    Isolate.WhenCalled(()=> fakeIB.GetId()).WillReturn("fakeID"); 
    Isolate.WhenCalled(() => fakeIB.GetKey()).WillReturn("fakeKey"); 
    Isolate.WhenCalled(() => fakeIB.GetToken()).WillReturn("success"); 

    // Act 
    var result = realA.functionA(); 

    // Assert 
    Assert.AreEqual("success", result); 
} 
関連する問題