Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq and accessing called parameters

I've just started to implement unit tests (using xUnit and Moq) on an already established project of mine. The project extensively uses dependency injection via the unity container.

I have two services A and B. Service A is the one being tested in this case. Service A calls B and gives it a delegate to an internal function. This 'callback' is used to notify A when a message has been received that it must handle.

Hence A calls (where b is an instance of service B):

b.RegisterHandler(Guid id, Action<byte[]> messageHandler);

In order to test service A, I need to be able to call messageHandler, as this is the only way it currently accepts messages.

Can this be done using Moq? ie. Can I mock service B, such that when RegisterHandler is called, the value of messageHandler is passed out to my test?

Or do I need to redesign this? Are there any design patterns I should be using in this case? Does anyone know of any good resources on this kind of design?

like image 609
laurencer Avatar asked Jan 14 '10 11:01

laurencer


1 Answers

You can get an instance of the callback (or any other input parameter) by using the Callback (the name similarity is incidental) method on the Mock:

[TestMethod]
public void Test19()
{
    Action<byte[]> callback = null;

    var bSpy = new Mock<IServiceB>();
    bSpy.Setup(b => b.RegisterHandler(It.IsAny<Guid>(), It.IsAny<Action<byte[]>>()))
        .Callback((Guid g, Action<byte[]> a) => callback = a);

    var sut = new ServiceA(bSpy.Object);
    sut.RegisterCallback();

    Assert.AreEqual(sut.Do, callback);
}

This works when ServiceA is defined as this:

public class ServiceA
{
    private readonly IServiceB b;

    public ServiceA(IServiceB b)
    {
        if (b == null)
        {
            throw new ArgumentNullException("b");
        }

        this.b = b;
    }

    public void RegisterCallback()
    {
        this.b.RegisterHandler(Guid.NewGuid(), this.Do);
    }

    public void Do(byte[] bytes)
    {
    }
}
like image 59
Mark Seemann Avatar answered Sep 18 '22 17:09

Mark Seemann