如何使用 Moq 测试 lambda 函数?

发布于 2024-09-19 20:36:20 字数 1227 浏览 7 评论 0 原文

有一个函数:

public class MyCacheClass : ICache
{
    public void T GetObject<T>(Func<T> func)
    {
        T t;
        ...
        t = func();
        ...
        return t;
    }
}

public class MyWorkClass : IWork
{
    public Object MyWorkMethod(string value)
    {
        return new object();
    }
}

这些函数按以下方式调用:

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        ...
        Object obj = cache(()=>work.MyWorkMethod(strVal));
        ...
    }
}

有必要为此编写一个 UnitTest(带有 Moq),并检查传递到“MaCacheClass.GetObject”的参数是否正确。

它应该是这样的:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(...)).Times.Once());
}

我如何提供适合“lambda 函数”的参数? 还有其他选择吗?

欢迎任何想法。

There is a function:

public class MyCacheClass : ICache
{
    public void T GetObject<T>(Func<T> func)
    {
        T t;
        ...
        t = func();
        ...
        return t;
    }
}

public class MyWorkClass : IWork
{
    public Object MyWorkMethod(string value)
    {
        return new object();
    }
}

These functions are been called in the following way:

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        ...
        Object obj = cache(()=>work.MyWorkMethod(strVal));
        ...
    }
}

It is necessary to write a UnitTest (with Moq) for that and check if proper parameter as passing into 'MaCacheClass.GetObject'.

It should be something like this:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(...)).Times.Once());
}

How could I provide parameter that will fit as 'lambda-function'?
Are there any other options?

Any thoughts are welcome.

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

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

发布评论

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

评论(2

不疑不惑不回忆 2024-09-26 20:37:02

您可以更改该类,以便将 lambda 传递到该类中,而不是整个 IWorkClass。然后您就可以存储并验证实际的 lambda。

或者,您可以使用回调机制实际调用传入缓存的 lambda,然后验证是否在该模拟上调用了 IWorkClass.MyWorkMethod。当缓存有价值时,这与缓存和工作类最终的使用方式相匹配,因此它还可以提供一个更好的有价值行为的示例,而不仅仅是单独测试方法。

第三,您可以只使用 It.IsAny>() 并接受您必须通过检查来获得正确的位(只要更容易获得它)对的比错的多,这通常是可以的)。我通常会命名我的委托或 lambda 签名,因此我可能会遇到这个 Func 问题。会成功的。

作为第四种替代方案,推出一个小存根缓存,而不是使用模拟框架,然后从中获取并调用 lambda。尽管我非常喜欢 Moq 及其 Java 对应项 Mockito,但有时我发现,当我们承认这些工具不支持我们的功能时,单元测试更容易阅读和维护。正在尝试与他们做些什么。它只有几行代码,并且存根缓存很可能对其他单元测试也很有用。

You could change the class so that you pass the lambda into the class, rather than the entire IWorkClass. Then you'd be able to store and verify against that actual lambda.

Alternatively you could use the Callback mechanism to actually call the lambda which gets passed in to the cache, then verify that IWorkClass.MyWorkMethod got called on that mock instead. That matches how the cache and work class eventually get used, when the cache is valuable, so it could also provide a better example of the valuable behaviour than just testing the method in isolation.

Third, you could just use It.IsAny<Func<string, void>>() and accept that you'll have to get that bit right by inspection (as long as it's easier to get it right than wrong this is usually OK). I normally name my delegate or lambda signatures so I may have got this Func<string, void> thing wrong. It'll work out.

As a fourth alternative, roll out a little stub cache instead of using a mocking framework, then grab and call the lambda from that. Much though I love Moq and its Java counterpart Mockito, sometimes I find unit tests are far easier to read and maintain when we just accept that the tools don't support what we're trying to do with them. It's only a few lines of code, and the chances are that the stub cache will be useful for other unit tests too.

绝影如岚 2024-09-26 20:36:54

选项之一可能是:而不是在 Funct 参数上传递一些实现带有“GetObject”函数的接口的对象,例如:

public interface IGetObject
{
    T GetObject<T>();
}

public class MyGetObject : IGetObject
{
    public MyGetObject()
    {
    }

    public void Init(string strVal, Func<string, T> func)
    {
        _strVal = strVal;
        _func = func;
    }

    public T GetObject<T>()
    {
        return _func(_strVal);
    }
}

public class MyCacheClass : ICache
{
    public void T GetObject<T>(IGetObject getter)
    {
        T t;
        ...
        t = getter.GetObject<T>();
        ...
        return t;
    }
}

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        IGetObject getter = UnityContainer.Resolve<IGetObject>();
        getter.Init(strVal, str=>work.MyWorkMethod(str));
        ...
        Object obj = cache.GetObject(getter);
        ...
    }
}

在这种情况下,我想,我们还需要将 UnityContainer 注入到测试对象中,因此它将是类似的东西:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();
    Mock<IGetObject> mockGetter  = new Mock<IGetObject>();

    mockGetter.Setup(mock=>mock.GetObject()).Return(new Object());

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(mockGetter.Object)).Times.Once());
}

并为“MyCacheClass”的“GetObject”方法提供额外的测试,该测试将检查它是否调用 IGetObject 参数的“GetObject”方法...

PS 有点复杂的解决方案...所以,如果有人看到更好的解决方案,请建议!

One of the option could be: instead on Funct parameter pass some object that implement interface with 'GetObject' function, for example:

public interface IGetObject
{
    T GetObject<T>();
}

public class MyGetObject : IGetObject
{
    public MyGetObject()
    {
    }

    public void Init(string strVal, Func<string, T> func)
    {
        _strVal = strVal;
        _func = func;
    }

    public T GetObject<T>()
    {
        return _func(_strVal);
    }
}

public class MyCacheClass : ICache
{
    public void T GetObject<T>(IGetObject getter)
    {
        T t;
        ...
        t = getter.GetObject<T>();
        ...
        return t;
    }
}

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        IGetObject getter = UnityContainer.Resolve<IGetObject>();
        getter.Init(strVal, str=>work.MyWorkMethod(str));
        ...
        Object obj = cache.GetObject(getter);
        ...
    }
}

In this case, I guess, we need also to inject UnityContainer into tested object, therefore it will be something like that:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();
    Mock<IGetObject> mockGetter  = new Mock<IGetObject>();

    mockGetter.Setup(mock=>mock.GetObject()).Return(new Object());

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(mockGetter.Object)).Times.Once());
}

AND provide additional test for 'GetObject' method of 'MyCacheClass' that will check if it calls 'GetObject' method of the IGetObject parameter...

P.S. A little bit complicated solution... so, if anybody see the better one, please advise!

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