开发者

How to test lambda functions with Moq?

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.


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!


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜