web-dev-qa-db-ja.com

Moqを使用して拡張メソッドをモックするにはどうすればよいですか?

私は拡張メソッドの結果に依存するテストを書いていますが、その拡張メソッドの将来の失敗がこのテストを破ることを望んでいません。その結果をあざけることは明らかな選択のように見えましたが、Moqは静的メソッドをオーバーライドする方法を提供していないようです(拡張メソッドの要件)。 Moq.ProtectedとMoq.Stubにも同様のアイデアがありますが、このシナリオには何も提供されていないようです。私は何かを見逃していますか、これについて別の方法で行く必要がありますか?

これは、通常の)「オーバーライドできないメンバーに対する無効な期待」で失敗する些細な例です。これは、拡張メソッドをモックする必要があるという悪い例ですが、そうすべきです。

public class SomeType {
    int Id { get; set; }
}

var ListMock = new Mock<List<SomeType>>();
ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5))
        .Returns(new SomeType { Id = 5 });

TypeMockのジャンキーについては、代わりにIsolatorを使用することをお勧めします。TypeMockが目隠しと酔いつぶれた仕事をするように見えるので、その努力に感謝していますが、予算はすぐには増えません。

75
patridge

拡張メソッドは、変装した静的メソッドにすぎません。 MoqやRhinomocksのようなフレームワークのモックでは、オブジェクトのモックインスタンスのみを作成できます。つまり、静的メソッドのモックは不可能です。

59
Mendelt

拡張メソッドのコードを変更できる場合は、テストできるように次のようにコーディングできます。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

public static class MyExtensions
{
    public static IMyImplementation Implementation = new MyImplementation();

    public static string MyMethod(this object obj)
    {
        return Implementation.MyMethod(obj);
    }
}

public interface IMyImplementation
{
    string MyMethod(object obj);
}

public class MyImplementation : IMyImplementation
{
    public string MyMethod(object obj)
    {
        return "Hello World!";
    }
}

そのため、拡張メソッドは実装インターフェースのラッパーにすぎません。

(構文糖のような拡張メソッドなしで、実装クラスのみを使用できます。)

また、実装インターフェイスをモックして、拡張機能クラスの実装として設定できます。

public class MyClassUsingExtensions
{
    public string ReturnStringForObject(object obj)
    {
        return obj.MyMethod();
    }
}

[TestClass]
public class MyTests
{
    [TestMethod]
    public void MyTest()
    {
        // Given:
        //-------
        var mockMyImplementation = new Mock<IMyImplementation>();

        MyExtensions.Implementation = mockMyImplementation.Object;

        var myObject = new Object();
        var myClassUsingExtensions = new MyClassUsingExtensions();

        // When:
        //-------
        myClassUsingExtensions.ReturnStringForObject(myObject);

        //Then:
        //-------
        // This would fail because you cannot test for the extension method
        //mockMyImplementation.Verify(m => m.MyMethod());

        // This is success because you test for the mocked implementation interface
        mockMyImplementation.Verify(m => m.MyMethod(myObject));
    }
}
21
informatorius

この質問は約1年間アクティブではありませんでしたが、Microsoftは Moles と呼ばれるまさにこれを処理するフレームワークをリリースしました。

いくつかのチュートリアルもあります。

  • DimeCasts.net
  • 15
    Mike Fielden

    モックに必要な拡張メソッドのラッパークラスを作成しました。

    public static class MyExtensions
    {
        public static string MyExtension<T>(this T obj)
        {
            return "Hello World!";
        }
    }
    
    public interface IExtensionMethodsWrapper
    {
        string MyExtension<T>(T myObj);
    }
    
    public class ExtensionMethodsWrapper : IExtensionMethodsWrapper
    {
        public string MyExtension<T>(T myObj)
        {
            return myObj.MyExtension();
        }
    }
    

    次に、IOCコンテナを使用して、テストおよびコードでラッパーメソッドをモックできます。

    12
    ranthonissen

    拡張メソッドの場合、通常は次のアプローチを使用します。

    public static class MyExtensions
    {
        public static Func<int,int, int> _doSumm = (x, y) => x + y;
    
        public static int Summ(this int x, int y)
        {
            return _doSumm(x, y);
        }
    }
    

    _doSummをかなり簡単に注入できます。

    4
    dmigo