2013-09-22 8 views
7

は、以下のクラスC#でテストするための内部メソッドをいくつか保持するインターフェイスを作成するには?あなたはそれが<code>public</code>方法と<code>internal</code>方法を持って見ての通り

public class Entity { 
    public void Foo() { ... } 
    internal void Bar() { ... } 
} 

を考えてみましょう。今、このクラスをテストで(このアセンブリと他のアセンブリの両方で)模擬するためのインターフェイスを作成したいと思います。コードを次のように書き換えます。

public interface IEntity { 
    void Foo(); 
} 

internal class Entity : IEntity { 
    public void Foo() { ... } 
    public void Bar() { ... } 
} 

ただし、これは別の問題を引き起こします。同じアセンブリ内の別の方法でクラスを使用する場合、私はもうBarを呼び出すことはできません。

public class OtherClass { 
    public void SomeMethod(IEntity entity) { 
    entity.Bar(); // error! 
    } 
} 

このようなコードを書き換える:

public class OtherClass { 
    public void SomeMethod(IEntity entity) { 
    (entity as Entity).Bar(); // error in test! 
    } 
} 

トリガーユニットテストでエラーをトリガするSomeMethod 。同じアセンブリ内でinternalのメソッドを引き続き使用できるようにコードを書き直すにはどうすればよいですか?パブリックメンバーのみを他のアセンブリに公開することはできますか?

更新:クラスOtherClassは、直接ユーザーに公開されていないEntityクラスの内部、上で動作する必要があるユーティリティクラスです。ただし、このクラス自体はユーザーに公開されているため、間接的にはEntityの内部にアクセスできます。 SomeMethodは、ユーザーがEntityオブジェクトの内部状態を壊さないようにするために必要なチェックを実行するため、これが望ましいです。

+0

インターフェイスを作成する代わりにメソッドを仮想にするのはなぜですか? – adrianm

+0

@adrianm:私はまた、ユーザーのための明確なインターフェースを必要とするので、プライベート/内部メンバーと実装が混在することはありません。また、私はすべての依存プロジェクトを再構築することなく、将来的には別の実装を提供できるようにしたいと考えています。しかし、内部メンバーを扱うときに作成するすべての困難を考慮して、私は上記の利点にもかかわらずこのオプションを選択するかもしれません。 –

答えて

4

public class MyClassWithInternalMethod 
{ 
    internal object GetSomething() 
    { 
     return null; 
    } 
} 

これをテストするために、あなたがこのようにそれを模擬することができます: は、あなたのクラスは次のようになりますと言うことができますテスト用:

public interface IEntity 
{ 
    //Implementation 

} 

internal interface ITestEntity : IEntity 
{ 
    void TestMethod(); 
} 


class Entity: ITestEntity 
{ 
    // 
} 
+0

これは素晴らしい考えですが、私の心にも来ましたが、うまくいきません。私の質問にあなたの例をマッチさせるために、 'IEntity'は' Foo'を公開し、 'ITestEntity'は内部' ITestEntity'を公開 'SomeMethod'に渡す必要がある' Bar'を公開します。これにより、一貫性のないアクセシビリティエラーが発生します。 –

+1

私は参照してください。しかし、 'IEntity'を渡すと' ITestEntity'にキャストして( 'ITestEntity'を模倣すべきです)、そのメソッドを使用することができます。私はそれがあまり優雅ではないことを認めます。 –

+0

これは正しいと思います...ユーザーは 'Mock 'を 'SomeMethod'に渡すことはありません。ユニットテストを書かないからです。代わりに 'Entity'だけを渡します。私はこれが実際に問題を解決するかもしれないと思います。これがうまくいくかどうかテストしてみましょう。 –

2

ユニットテストでのみ内部メソッドを呼び出すとしますか?そうでなければ、このメソッドを他のアセンブリに公開すると、internalをpublicのものに置き換える必要があります。

一般的な単体テストの場合、プライベートメソッドまたは内部メソッドをテストしないと、常に良いパターンになります。単体テストは実際にはパブリックインターフェイスをテストするだけで、ビジネスクラスは内部的な処理を行うパブリックメソッドを提供する必要があります。 内部メソッドをテストするには、そのパブリックな表現をテストする必要があります。

しかし、とにかく。プライベートまたは内部アクセサーをテストする場合、Visual Studio Testプロジェクトは通常、アクセサーラッパーを生成します。 クラスの通常のctorの代わりにアクセサのctorを呼び出す必要があります。そうすると、そのインスタンスの任意のプライベートまたは内部のメソッド/プロパティにアクセスできます。 http://msdn.microsoft.com/en-us/library/bb385974(v=vs.100).aspx

::編集:

ここで、これらの生成された型についてさらに詳しく議論した後、私はUnityとUnityAutoMoq(3異なるNugets)と一緒にMOQを使用する方法のあなたのための例があります。

内部ITestEntityインタフェースを使用してIEntityインターフェースを拡張:編集

using (var moqUnityContainer = new UnityAutoMoqContainer()) 
{ 
    moqUnityContainer.GetMock<MyClassWithInternalMethod>().Setup(p => p.GetSomething()).Returns(null); 
} 
+0

いいえ同じアセンブリ内の他のメソッドで内部メソッドを呼び出したいとします。私は内部メソッドをテストしようとしていません - 私はちょうど他のクラスが実際にそれを呼び出すことを確認するためにそれを嘲笑したいです。 –

+0

あなたはInternalsVisibleTo属性を使用しようとしましたか? http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx – MichaC

+0

「InternalsVisibleTo」がどのように役立つかわかりません。私は他のアセンブリへのアクセスを提供するのではなく、内部へのアクセスをアセンブリに制限したい。これはデフォルトではそうですが、テストのためにインターフェイスの背後にあるクラスを非表示にする必要があります。内部のアクセス指定子を持つことはできません。 –

13

コードを書き直して、同じアセンブリ内で内部メソッドを使用することができますが、ほかのアセンブリにパブリックメンバのみを公開できるようにするにはどうすればよいですか?

インターフェイスを内部で作成し、明示的に実装します。

internal interface ITest 
{ 
    void Foo(); 
    void Bar(); 
} 

public class Thing : ITest 
{ 
    void ITest.Foo() { this.Foo(); } 
    void ITest.Bar() { this.Bar(); } 
    public Foo() { ... } 
    internal Bar() { ... } 
} 

だから今publicクラスThingは、唯一のパブリックメソッドFooを持っています。アセンブリ外のコードは、BarITestの両方が内部であるため、Barを直接またはインターフェイスへの変換を介して呼び出すことはできません。

ベースクラスは、少なくともその派生クラスほど目に見える必要があります。そうではありません。少数の人々は、これが合法であることを知っている:

class C : C.I 
{ 
    private interface I {} 
} 

クラスは唯一それがを見ることができるインタフェースを実装することができます!これはちょっと変わったことですが、それが理にかなっている状況がいくつかあります。

+1

興味深い考えですが、ユーザーはアセンブリの外で 'Thing'クラスをモックできません。 'Thing'を使ってクラスを書くかもしれませんし、おそらくユニットテストもしたいと思うでしょう。 –

+6

@SergiyByelozyorov:あなたはそれを両方向にすることはできません。 「バー」はアセンブリの外側からアクセス可能であるか、アクセスできない。 –

+0

外部にはアクセスできないようにしてください。ユーザは 'Foo'を模倣する必要があるのは' Bar'の存在を知らないからです。 –

関連する問題

 関連する問題