views:

149

answers:

2

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?

A: 

Yes you can setup the Moq object to respond to expected and unexpected operations. Here's a Visual Studio Unit Test example...

[TestMethod]
public void MyTest()
    {
      var moqObject = new Mock<ServiceB>();

      // Setup the mock object to throw an exception if a certain value is passed to it...
      moqObject.Setup(b => b.RegisterHandle(unexpectedValue).Throws(new ArgumentException());

      // Or, setup the mock object to expect a certain method call...
      moqObject.Setup(b => b.RegisterHandle(expectedValue));

      var serviceA = new ServiceA(moqObject.Object);

      serviceA.DoSomethingToTest();

      // This will throw an exception if an expected operation didn't happen...
      moqObject.VerifyAll();
    }
Chris Arnold
+1  A: 

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)
    {
    }
}
Mark Seemann