模拟任何给定类型参数的泛型方法调用

发布于 2024-10-22 01:18:29 字数 406 浏览 1 评论 0原文

我有一个接口,

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());

这当然不起作用,因为我不能只是给起订量提供任何类型参数,我不知道必须返回哪种类型

对此有什么想法吗?

I have an interface

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

I'd like to mock it in a way, that it would just return a new instance of a given type, regardless of the exact type, something like:

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

(which doesn't work of course, because I cannot just give any type parameter to moq, and I can't know which type must be returned.

Any ideas on this one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

孤芳又自赏 2024-10-29 01:18:29

除了使用模拟之外,也许您的情况最好使用 存根

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

如果您确实需要模拟(以便您可以验证是否调用了 GetDataDocument)。有时,直接创建一个 Mock 类会更容易,而不是尝试使用 Mocking 框架。

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);

Instead of using a mock, maybe your case would be better to use a Stub.

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

If you truly need a mock (so you can verify that GetDataDocument was called). Instead of trying to wrestle with a Mocking framework it sometimes is easier to just create a Mock class out right.

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();
    }
}

And than in your test:

bool wasCalled = false;
IDataProvider dataProvider = new MockDataProvider(() => { wasCalled = true; });
var aTable = dataProvider.GetDataDocument<ATable>(new Guid());
Debug.Assert(wasCalled);
極樂鬼 2024-10-29 01:18:29

使用 Moq 4.13 或更高版本,您可以使用

  • It.IsAnyType — 匹配任何类型
  • It.IsSubtype — 匹配 T 和 T 的正确子类型
  • It.IsValueType< /code> — 只匹配值类型

要获取泛型参数的值或使用原始方法执行其他操作,您可以使用 InvocableActionIInitation 参数>InvocableFunc

  • setup.Callback(new InvocableAction(invocation => ...))
  • setup.Returns(new InvocableFunc(invocation => ...))代码>

这是一个例子:

var myMock = new Mock<IDataProvider>();
myMock.Setup(m => m.GetDataDocument<It.IsAnyType>(It.IsAny<Guid>()))
    .Returns(new InvocationFunc(invocation =>
    {
        var type = invocation.Method.GetGenericArguments()[0];
        return Activator.CreateInstance(type);
    }));

With Moq 4.13 or later you can use

  • It.IsAnyType — matches any type
  • It.IsSubtype<T> — matches T and proper subtypes of T
  • It.IsValueType — matches only value types

To get the value of the generic argument or do some other operation with the original method, you can use IInvocation parameter of InvocationAction or InvocationFunc

  • setup.Callback(new InvocationAction(invocation => ...))
  • setup.Returns(new InvocationFunc(invocation => ...))

Here is an example:

var myMock = new Mock<IDataProvider>();
myMock.Setup(m => m.GetDataDocument<It.IsAnyType>(It.IsAny<Guid>()))
    .Returns(new InvocationFunc(invocation =>
    {
        var type = invocation.Method.GetGenericArguments()[0];
        return Activator.CreateInstance(type);
    }));
不可一世的女人 2024-10-29 01:18:29

对于您要使用此模拟进行的特定测试,您可能知道 T 是什么,对吧?

只需为此设置模拟:

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

无论如何,实际上并不建议重用模拟,因此请继续为手头的实际测试设置模拟。

For the particular test you are going to use this mock for, you probably know what T will be, right?

simply just setup the mock for that:

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

It's not really recommended to reuse the mocks anyway, so go ahead and setup mocks for the actual test at hand.

笑饮青盏花 2024-10-29 01:18:29

我遇到了类似的问题,我选择在这种情况下不使用存根,因为我不希望对正在测试的接口进行添加,从而需要立即更改测试代码。即添加新方法不应破坏我现有的测试。

为了让模拟工作,我在运行时在给定的程序集中添加了所有公共类型。

//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 )
{
}

I had a similar issue, I chose against using a stub in this situation as I did not want additions to the interface being tested to require immediate changes to the test code. i.e. adding a new method should not break my existing tests.

To get the mock working I added all the public type in a given assembly at runtime.

//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 )
{
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文