Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define method implementation in mock object using Moq

Tags:

c#

mocking

moq

This is the situation. I had async call so I needed to make Mid tier for this in order to be able to test it.

request.BeginGetResponse(new AsyncCallback(LoginCallback), requestState);

So, in order to be able to test this without real request, I created interface which I can mock.

public interface IRequestSender
    {
        void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState);
    }

Then in implementation I can use call like that one above and I can provide some mock class to call my callback method regardless request is valid or not. My mock class looks like this.

public class RequestSenderMock : IRequestSender
    {
        public void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
        {
            var result = new Mock<IAsyncResult>();
            result.Setup(x => x.AsyncState).Returns(requestState);
            internalCallback(result.Object);
        }
    }

I can now easily create mock object in my unit test and use it. But when I create

var sender = new Mock<RequestSenderMock>();

I'm unable to verify call count for this object.

sender.Verify(x => x.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()), Times.Once());

It says that my method needs to be virtual. Is there a way to do this without making my method virtual? It would be best if I could somehow specify method impelementation when using interface.

var sender = new Mock<IRequestSender>();

And that somehow with Setup method or some other to make implementation on this mock object. Than I'll simply remove my mock class. Is this possible? What do you suggest?

like image 908
Vajda Avatar asked Jul 26 '11 17:07

Vajda


1 Answers

I find it confusing that you are creating a manual mock and using a mocking-framework to mock it (mocking a mock). I would consider moving your custom functions into some utility class and using callbacks instead.

Example:

public class RequestSenderHelpers
{
    public static void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
    {
        var result = new Mock<IAsyncResult>();
        result.Setup(x => x.AsyncState).Returns(requestState);
        internalCallback(result.Object);
    }

}

    [Test]
    public void Callback_VerifyingWithMethodImplementation_VerifyWorks()
    {
        // arrange
        var sender = new Mock<IRequestSender>();
        sender.Setup(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>())).Callback<HttpWebRequest, AsyncCallback, object>(RequestSenderHelpers.Send);

        // act
        sender.Object.Send(null, delegate {}, null);

        // assert
        sender.Verify(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()));
    }

To avoid the verbose setup you can wrap the setup of the method in an extension method and change your test accordingly:

public static class RequestSenderHelpers
{
    public static void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
    {
        var result = new Mock<IAsyncResult>();
        result.Setup(x => x.AsyncState).Returns(requestState);
        internalCallback(result.Object);
    }

    public static void SetupSendWithMockedAsyncResult(this Mock<IRequestSender> sender)
    {
        sender.Setup(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>())).Callback<HttpWebRequest, AsyncCallback, object>(Send);            
    }

}

    [Test]
    public void Callback_VerifyingWithMethodImplementation_VerifyWorks()
    {
        // arrange
        var sender = new Mock<IRequestSender>();
        sender.SetupSendWithMockedAsyncResult();

        // act
        sender.Object.Send(null, delegate {}, null);

        // assert
        sender.Verify(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()));
    }
like image 198
Marius Avatar answered Sep 20 '22 06:09

Marius