web-dev-qa-db-ja.com

特定の型パラメーターのジェネリックメソッド呼び出しのモック

インターフェースがあります

public interface IDataProvider
{
    T GetDataDocument<T>(Guid document) where T:class, new()
}

正確なタイプに関係なく、特定のタイプの新しいインスタンスを返すような方法でモックしたいと思います:

myMock.Setup(m => m.GetDataDocument<It.IsAny<Type>()>(It.IsAny<Guid>()))
.Returns(() => new T());

(もちろん、moqに型パラメーターを与えることはできず、どの型を返さなければならないかわからないため、これは機能しません。

これに関するアイデアはありますか?

44
Hassan

モックを使用する代わりに、 スタブ を使用した方が良い場合があります。

public class StubDataProvider : IDataProvider
{
    public T GetDataDocument<T>(Guid document) where T : class, new()
    {
        return new T();
    }
}

モックが本当に必要な場合(したがって、GetDataDocumentが呼び出されたことを確認できます)。 Mockingフレームワークと格闘する代わりに、Mockクラスを正しく作成する方が簡単な場合があります。

public class MockDataProvider : IDataProvider
{
    private readonly Action _action;

    public MockDataProvider(Action action)
    {
        _action = action;
    }

    public T GetDataDocument<T>(Guid document) where T : class, new()
    {
        _action();
        return new T();
    }
}

そして、あなたのテストよりも:

bool wasCalled = false;
IDataProvider dataProvider = new MockDataProvider(() => { wasCalled = true; });
var aTable = dataProvider.GetDataDocument<ATable>(new Guid());
Debug.Assert(wasCalled);
29
Mark Coleman

このモックを使用する特定のテストでは、おそらくTが何であるかを知っていますよね?

単にそのためのモックをセットアップするだけです:

myMock.Setup(m => m.GetDataDocument<MyDataClass>()>(It.IsAny<Guid>()))
   .Returns(() => new MyDataClass());

とにかくモックを再利用することはお勧めしませんので、実際のテストのためにモックをセットアップしてください。

11
Mikael Östberg

私は同様の問題を抱えていました。この状況ではスタブを使用しないことを選択しました。テスト対象のインターフェースに追加してテストコードをすぐに変更する必要がないようにしたからです。つまり、新しいメソッドを追加しても、既存のテストが中断されることはありません。

モックを機能させるために、実行時に特定のアセンブリにすべてのパブリックタイプを追加しました。

//This is fairly expensive so cache the types
static DummyRepository()
{
    foreach( var type in typeof( SomeTypeInAssemblyWithModelObjects ).Assembly.GetTypes() )
    {
        if( !type.IsClass | type.IsAbstract || !type.IsPublic || type.IsGenericTypeDefinition )
        {
            continue;
        }

        g_types.Add( type );
    }
}

public DummyRepository()
{
    MockRepository = new Mock<ISomeRepository>();

    var setupLoadBy = GetType().GetMethod( "SetupLoadBy", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod );

    foreach( var type in g_types )
    {
        var loadMethod = setupLoadBy.MakeGenericMethod( type );
        loadMethod.Invoke( this, null );
    }
}

private void SetupLoadBy<T>()
{
    MockRepository.Setup( u => u.Load<T>( It.IsAny<long>() ) ).Returns<long>( LoadById<T> );
}

public T LoadById<T>( long id )
{
}
6
Andre