web-dev-qa-db-ja.com

コンストラクターにパラメーターがある場合にMoqを使用してモックオブジェクトを実装する

私はこれを読んだ answer Ragzitsuによる同じ質問について。私はまだ物事を実装する方法をまだ混乱しています。誰かが実装の例を教えてもらえますか。

次のクラスがあります。

class Fizz : IFizz
{
}

class Buzz : IBuzz
{

}

class Bar : IBar
{

}

class Foo : IFoo
{
    public Foo(IBar bar, IFizz fizz, IBuzz buzz)
    {
        //initialize etc.
    }

    //public methods
}

ここでコンストラクタを回避する実用的な方法は何ですか?私は次のようなことをしたい

var foo = new Mock<IFoo>();

言い換えれば、コードはアドバイスの後にどのように見えますか

The best thing to do would be right click on your class and choose Extract interface.

22
happygilmore

以下に示すように、MockBehaviorを参照することにより、コンストラクターにパラメーター引数があるモックを作成できます。

Mock<testClass>(MockBehavior.Strict, new object[] {"Hello"}); 
26
Ashraf Alam

TL; DR

あなたが本当に何をしようとしているのかは不明です。

インターフェイスIFoo(これはあなたの質問が示すようです)をすでに定義しており、それを別のタイプにモックする必要がある場合は、すでに設定されています:var bazz = new Bazz(new Mock<IFoo>());

依存関係として別の型に提供する必要があるインターフェイスに抽象化していない具体的なクラスFooがある場合、インターフェイスを抽出することは、Bazzオブジェクトを作成せずに初期化できる最も一般的な方法の1つです。実際のFooクラス。実際のコードがどのように見えるかに応じて、IFooが依存するFooの部分のみをBazzに抽象化することにより、スコープを制限できます。もう1つの方法は、コンクリートをモックし、使用する コンストラクター引数 をmoqに提供することです。セットアップとリセットをモックに提供するために、Fooの依存関係部分が「モック可能」(パブリック仮想)であることを確認する必要があります。

編集:

実際の質問に答えるための編集として

コードはアドバイスの後にどのように見えますか?最善のことは...インターフェイスを抽出することです?

インターフェイスを抽出するためのアドバイスは、探しているものとは異なる場合があります。他の回答で述べたように、私たちは実際にテストしようとしているものが何かわからないです。

テストする場合具体的な機能FooIFoo実装インターフェースの抽出とIFooのモックは、moqがインターフェース自体を実装するため、実際には役に立ちませんそして、その実装のセットアップメソッドとプロパティで提供するように指示した「機能」のみを提供します。これがあなたがしていることである場合、[〜#〜] and [〜#〜]テスト中のメソッドは、具体的なFoo実装で他のメソッドを呼び出します[〜#〜] and [〜#〜]このメソッドは抽象化を提供したいものです。@ Chris Marisicが述べたように、抽象化するメソッドをvirtualに変更します。これを仮想として、canは、モックフレームワークを使用して抽象化を提供するか、過去に行ったようにテストクラスのテスト対象からサブタイプを作成します。

_public class MyFooTests
{
    ... // Tests
    private class SubFoo : Foo
    {
        public override void MethodToAbstract(bool par)
        {
            // do something expected
        }
    }
}
_

このメソッドを使用すると、Fooのコンストラクターに何かを提供する必要がありますが、それはこれらのインターフェイスでモックを使用する場合の主なケースです。

ここで、Fooをテストしていて、テスト対象のメソッドがIBarIFizzIBuzzのメソッドを呼び出しているだけで、他に作成したい抽象化がない場合は、すでにセットアップされています。これらのインターフェイスをモックし、特定のテストケースで期待するものを返すように設定し、Fooにモックオブジェクトを提供します。

抽出インターフェースのアドバイスは、@ Chris Marisicによると、テスト対象がFooに依存している場合に非常に役立ちます。ただし、この状況でインターフェイスを作成したくないという彼の感情は共有できません。それはあなたにもっと臭いがするものに依存します:モック機能を提供するためのインターフェースを作成するか、同じものを提供するために修飾子を変更します。モディファイヤを変更し、moqを使用して具体的なFooをモックする場合は、デフォルトのコンストラクタを公開する(pretty smelly to me)か、指定されたコンストラクタ引数でモックを作成する必要がありますあなたの 参照された答え で述べられているように、仕様。

次のものがあるとしましょう:

_public class Baz
{
    private Foo foo;

    public Baz(Foo inputFoo)
    {
        this.foo = inputFoo;
    }

    public bool GetFromTheFoo()
    {
        // other stuff here
        var fooStuff = this.foo.GetStuff();
        // logic on the stuff;
        return fooStuff != null;
    }
}
_

上記のBazFooに依存しています。 Fooのインターフェースを抽出した後、BazIFooを受け入れる必要があります。

すると、Fooは質問のようになり、IFooは、Bazから抽象化するメソッドのシグネチャ定義を持ちます。

_object GetStuff();
_

これで、テストでvar foo = new Mock<IFoo>();を使用し、fooBazテストサブジェクトに指定できます。 IFoo.GetStuff()メソッドをセットアップすることを忘れないでください。

_foo.Setup(f => f.GetStuff()).Returns(new object());
_

IFoo抽象化を作成したため、必要はありません。

ここでコンストラクタを回避しますか?

IFooのモックには、moqが提供するデフォルトのコンストラクターのみがあるためです。

個人的には、IFizzIBuzz、およびIBarBazの依存関係チェーンから削除するため、適用する場合はインターフェイスアプローチを好みます。 BazFooに依存し、Fooが_IFizz etc._に依存する場合、Bazも_IFizz etc._に依存します。IFooを抽出した後、BazIFooのみに依存します。


元の回答(実際の回答ではありません #chagrin

少し前にこれが尋ねられたので、これを追加する価値はないかもしれませんが、将来の読者のために、moqを使用するときは次のことは同じだと考えないでください。

_var fooDouble = new Mock<IFoo>();
_

以下と同じではありません:

_var fooDouble = new Mock<Foo>();
_

私の理解では、IFooのモックを作成するとき、moqフレームワークはIFooインターフェースを実装するdoubleクラスを生成し、生成された実装で自身のデフォルトコンストラクターを提供します。ただし、Fooのモックは、生成されたコードにインターフェイスを直接実装せず、具体的なFooクラスから継承するため、スタブ化するメソッドを仮想化する必要があります。具体的な実装が必要な場合は、実際に使用する(たとえば、テスト対象のユニット)には、モックに「CallBase = true」が必要です。これがコンストラクター引数を持つ具象クラスで実行したい場合、デフォルトのコンストラクターの公開を避けるために@Palkinの答えを探しているかもしれません。

10
Josh Gust

インターフェイスIFooがあり、パラメーターを持つコンストラクターを持つFooクラスをモックする場合は、何も変更しないでください。

あなたのコードはまさにあなたが必要とするものです。

var foo = new Mock<IFoo>();

次のアドバイスは、クラスにインターフェースもパラメーターなしのコンストラクターもない場合の状況をカバーしています。実際、必要なすべてのパラメーターをMockのコンストラクターに渡すことができます。

var mock = new Mock<IFoo>("constructor", "arguments");

しかし

「最善の方法は、クラスを右クリックして、抽出インターフェイスを選択することです。」 (c)

8
Ilya Palkin

最善の方法は、クラスを右クリックして[抽出]インターフェースを選択することです。

この概念を直交的な意味で取り上げます。私はこの声明に同意しませんインターフェースは解決策ではありません問題の状況に対して。

前の質問のテキストに戻る:

public class CustomerSyncEngine {
    public CustomerSyncEngine(ILoggingProvider loggingProvider, 
                              ICrmProvider crmProvider, 
                              ICacheProvider cacheProvider) { ... }

    public void MethodWithDependencies() {
        loggingProvider.Log();
        crmProvider.Crm();
        cacheProvider.Cache();
    }
}

追加したメソッドに注意してください。

本当の質問は、CustomerSyncEngineを具体的にテストするのではなく、代わりにdepends on CustomerSyncEngineのクラスをテストするときだと思います。このクラスをSuperSyncEngineと呼びましょう。 SuperSyncEngineに対するテストの作成は、3つのインターフェイスとCustomerSyncEngineが持つ他の追加の依存関係とともに、SuperSyncEngine全体をモックアウトする必要があるため、苦痛になります。

テストしようとしているコードがSuperSyncEngineであり、これがCustomerSyncEngineに依存している場合、インターフェイスはここでの答えではありません。あなたはcouldICustomerSyncEngineを作成できますが、そのインターフェースはモックフレームワークのためだけに作成されるべきではありません。より良い解決策は、CustomerSyncEngine.MethodWithDependencies仮想

public virtual void MethodWithDependencies() {
    loggingProvider.Log();
    crmProvider.Crm();
    cacheProvider.Cache();
}

これにより、CustomerSyncEngineに付属する依存関係を無視して、このメソッドをモックフレームワークに置き換えることができます。

このアプローチに従えば、CustomerSyncEngineで公開されるデフォルトのコンストラクターが必要になります。あなたはそれを回避し、nullまたは他の値で依存関係を満たすことができますが、目標が摩擦を減らすことである場合は追加の作業になります。

7
Chris Marisic

これは一種の古い投稿ですが、同様の問題に遭遇しました(Moqから始めました)。他の誰かが同様の問題を抱えている場合、ここに私が持っていたものがあります:

class Bar : IBar
{
}

class Foo : IFoo
{
    public Foo(IBar bar)
    {
        //initialize etc.
    }

    //public methods
}

class Manager : IManager
{
    public Manager(Foo foo)
    {
        //initialize etc
    }
}

私がやろうとしているのは、ManagerではなくFooをテストすることです。

これがエラーを投げた最初のテストコードです。

[TestFixture]
public class ManagerTest
{
    [Test]
    public void SomeTest()
    {
        var fooMock = Mock<IFoo>();
        var managerUnderTest = new Manager(fooMock.Object);
    }
}

エラーはCastle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Something.Models.Foo. Could not find a parameterless constructor.

エラーメッセージを読むと、パラメーターなしのコンストラクターがないため、MoqはFooをインスタンス化する方法を理解しません。また、パラメーターを使用してコンストラクターをインスタンス化する方法をMoqに伝えません。 2番目のセクションを次のように変更します。

[TestFixture]
public class ManagerTest
{
    [Test]
    public void SomeTest()
    {
        var barMock = Mock<IBar>();
        var fooMock = Mock<IFoo>(barMock.Object);
        var managerUnderTest = new Manager(fooMock.Object);

        //proceed with test
    }
}
5
therealjumbo

FOoクラスをテストする場合は、モックする必要はありません。テストするクラスに依存するクラスのみをモックする必要があります。

何かのようなもの:

Mock<IBar> bar = new Mock<IBar>();
Mock<IBuzz> buzz = new Mock<IBuzz>();
Mock<IFizz> fizz= new Mock<IFizz>();

Foo foo = new Foo(bar.Object, buzz.Object, fizz.Object);

次に、テストするfooのメソッドを呼び出します;)

Fooのメソッドがbar/fuzzまたはfizz内のメソッドを使用する場合、次のようなsintaxを使用する必要があります。

buzz.Setup(x => x.DoSomething()).Returns(1);

このようにして、fooメソッドが呼び出されると、DoSomethingが呼び出され、常に1が返されます;)

3
Oscar Bralo

インターフェースは、1つのタイプに複数の実装がある場合に役立ちます。マイナス面では、これは、インターフェイスが必要なためにインターフェイスを派生させないように注意する必要があることを意味します(よくある間違い)。肯定的な面では、モックされたインターフェイスは定義上2番目の実装です。

したがって、結論は次のとおりです。型が他の型の依存関係として機能する場合、インターフェイスを実装するのが適切な候補です。その場合、単体テストでインターフェイスを自由に完全にモックできます。

関連する注意事項として、インターフェイスを定義するときは、インターフェイスに機能部分のみを追加するようにしてください。オブジェクトを定義するメソッドdoesではなく、オブジェクトlikesを定義します。多数のゲッター/セッターとのインターフェースを持つことは、デザインに価値を追加しません。これは理論の非常に大きな領域であり、この小さなウィンドウは、それについてさらに記述する場所ではありません。

あなたの質問との関係を明確にするために:モック化された実装は、インターフェースに必要な動作を提供する必要があります。そのためには、モックフレームワークの機能を使用します。これはFooクラスの具体的な実装とは関係ありません-Mockオブジェクトの特定の動作を定義します。

0
Zoran Horvat