1. ホーム
  2. unit-testing

[解決済み] Moqでクラスをモックするとき、特定のメソッドだけをCallBaseする方法は?

2022-02-15 03:56:34

質問

モックには本当に感謝しています。 Loose 期待値が設定されていないときにデフォルト値を返すモッキングの動作です。これは便利でコードの節約になりますし、安全対策としても機能します。ユニットテスト中に依存関係が意図せず呼び出されることはありません(それらが仮想である限り)。

しかし、テスト対象のメソッドがたまたま仮想であった場合、これらの利点をどのように保てばよいのか、私は混乱しています。
この場合、私は する は、クラスの残りの部分を緩くモック化したまま、そのメソッドの実際のコードを呼び出したいのです。

私が検索して見つけたのは、「Security」(セキュリティ)を設定することです。 mock.CallBase = true を使用して、そのメソッドが確実に呼び出されるようにします。しかし、それはクラス全体に影響します。なぜなら、呼び出しの依存性を隠すクラスの他のすべてのプロパティとメソッドについてジレンマに陥るからです。

  1. 依存性を隠すすべてのプロパティとメソッドのスタブをセットアップする -- テストがそれらの依存性を気にする必要がないと考えていても、です。
  2. スタブのセットアップを忘れないように(そして、将来的に新しい依存関係が追加されないように) -- ユニットテストが本当の依存関係にぶつかるリスクを考えてください。

私が欲しいと思っているのは、次のようなものです。
mock.Setup(m => m.VirtualMethod()).CallBase();
を呼び出すと mock.Object.VirtualMethod() Moqは実際の実装を呼び出すのです。

Q: Moqで、いくつかの依存関係をスタブするためにクラスをモックしたとき、仮想メソッドをテストする方法はありますか?I.e. CallBase=true そして、すべての依存関係をスタブする必要がありますか?


説明のためのコード例
(MSTest, InternalsVisibleTo DynamicProxyGenAssembly2 を使用)

次の例では TestNonVirtualMethod は通るが TestVirtualMethod は失敗し、null を返します。

public class Foo
{
    public string NonVirtualMethod() { return GetDependencyA(); }
    public virtual string VirtualMethod() { return GetDependencyA();}

    internal virtual string GetDependencyA() { return "! Hit REAL Dependency A !"; }
    // [... Possibly many other dependencies ...]
    internal virtual string GetDependencyN() { return "! Hit REAL Dependency N !"; }
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestNonVirtualMethod()
    {
        var mockFoo = new Mock<Foo>();
        mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);

        string result = mockFoo.Object.NonVirtualMethod();

        Assert.AreEqual(expectedResultString, result);
    }

    [TestMethod]
    public void TestVirtualMethod() // Fails
    {
        var mockFoo = new Mock<Foo>();
        mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);
        // (I don't want to setup GetDependencyB ... GetDependencyN here)

        string result = mockFoo.Object.VirtualMethod();

        Assert.AreEqual(expectedResultString, result);
    }

    string expectedResultString = "Hit mock dependency A - OK";
}

解決方法は?

Lunivoreさんの回答は、書かれた時点では正しかったと思います。

Moqの新しいバージョン(2013年のバージョン4.1以降だと思います)では、あなたが提案する構文そのもので、あなたが望むことを行うことが可能です。つまり、以下の通りです。

mock.Setup(m => m.VirtualMethod()).CallBase();

の基本実装を呼び出すためのルースモックを設定しています。 VirtualMethod を返すだけでなく default(WhatEver) しかし、このメンバー ( VirtualMethod ) のみです。


ユーザーのBornToCodeがコメントで指摘しているように、この方法はメソッドに 戻り値型 void . を指定すると VirtualMethod が非voidの場合 Setup を呼び出すと Moq.Language.Flow.ISetup<TMock, TResult> を継承しています。 CallBase() メソッドから Moq.Language.Flow.IReturns<TMock, TResult> . しかし、このメソッドが無効である場合、私たちは Moq.Language.Flow.ISetup<TMock> の代わりに、必要な CallBase() というメソッドがあります。

更新しました。 andrew.rockwellは以下のように書いています。 void というメソッドがあり、どうやらバージョン4.10(2018年~)で修正されたようです。