DEV Community

Scott Hannen
Scott Hannen

Posted on • Originally published at scotthannen.org on

Sometimes It's Easier to Mock Without Moq

I like Moq. It’s easy to use and it’s used frequently enoughthat developers are familiar with it. But sometimes there’s an even easier way to write tests, and thatinvolves creating classes that mock interfaces.

As an example (I’ll try not to convolute this) suppose we have a class that

  • listens to messages from a queue
  • reads the contents of the message and converts it into a command
  • invokes a command handler

I want to write a test to verify that given a message with specific contents,the command sent to the command handler has the expected properties.

Here’s what that test might look like using Moq. (Ignore the few details)

[TestMethod]
public async Task CommandHandlerInvokedWithCorrectCommand()
{
    var commandHandlerMock = new Mock<ICommandHandler<DoSomethingCommand>>();
    DoSomethingCommand handledCommand = null;
    commandHandlerMock.Setup(x =>
            x.HandleCommandAsync(It.IsAny<DoSomethingCommand>()))
        .Callback<DoSomethingCommand>(command => handledCommand = command);
    var subject = new MessageListener(commandHandlerMock.Object);
    var message = CreateMessage("X", 2);
    await subject.ReceiveMessage(message);

    Assert.IsTrue(handledCommand.SomeProperty == "x"
                  && handledCommand.SomeOtherProperty == 2);
}

Enter fullscreen mode Exit fullscreen mode

In case you haven’t used this, here’s what we’re doing:

  • Using Moq to create a Mock<ICommandHandler<DoSomethingCommand>>
  • Setting up the mock so that when HandleCommandAsync is called,it executes a callback that assigns the DoSomethingCommand parameterpassed to the command handler to the handledCommand variable so thatwe can inspect the command that was sent to the handler.

This isn’t bad. I’ve written it a bunch of times, and developers who’veused Moq know what it means. But can we make it a little simpler?Judge for yourself.

In the time it takes me to set up that mock, I can create this class:

public class MockCommandHandler<T> : ICommandHandler<T>
{
    public T HandledCommand { get; private set; }

    public async Task HandleCommandAsync(T command)
    {
        HandledCommand = command;
    }
}

Enter fullscreen mode Exit fullscreen mode

That took seconds. What does it do for the unit test?I think it’s an improvement.

[TestMethod]
public async Task CommandHandlerInvokedWithCorrectCommand()
{
    var commandHandler = new MockCommandHandler<DoSomethingCommand>();
    var subject = new MessageListener(commandHandler);
    var message = CreateMessage("X", 2);
    await subject.ReceiveMessage(message);
    var handledCommand = commandHandler.HandledCommand;
    Assert.IsTrue(handledCommand.SomeProperty == "x"
                  && handledCommand.SomeOtherProperty == 2);
}

Enter fullscreen mode Exit fullscreen mode

Isn’t that easier to read? It’s undeniably shorter. And I can reuse thatmock command handler in other tests. It also makes a difference if atest necessarily sets up more dependencies. We want to keep our tests asshort and easy to read as possible.

The Wall of Moq

As a side note, sometimes if classes have too many dependencies we declarea bunch of Moq mocks as private variables in our tests followed by lots of initialization code thatinstantiates them and sets them up, and still individual tests have to do different setup for some of them.All we see when we open the file is Moq.

That’s not a Moq problem - it’s a problem with the classes we’re testing. Theyhave too many dependencies. Anyway, I don’t like it. I call it “the Wall of Moq.”It makes tests difficult to read. We’ll see mocks set up with behaviors they don’tneed because no one can keep track of which ones are needed for any one test.Once that happens we can no longer understand either the tests or the codewe’re testing.

It’s Really Easy With Delegates

What if instead of an ICommandHandler<T> interface we have a delegate,and we inject that into our class? (More on that here).

If we inject this delegate into our message listener instead of an interface:

public delegate Task CommandHandler<T>(T command);

Enter fullscreen mode Exit fullscreen mode

…then we can use a local method as our mock:

[TestMethod]
public async Task CommandHandlerInvokedWithCorrectCommand()
{
    DoSomethingCommand handledCommand = null;
    async Task CommandHandlerMock(DoSomethingCommand command) => handledCommand = command;
    var subject = new MessageListener(CommandHandlerMock);
    var message = CreateMessage("X", 2);
    await subject.ReceiveMessage(message);
    Assert.IsTrue(handledCommand.SomeProperty == "x"
                  && handledCommand.SomeOtherProperty == 2);
}

Enter fullscreen mode Exit fullscreen mode

Now we don’t have to declare a separate MockCommandHandler, andwe can still declare and set up the mock in a single succint line.

It’s not a huge difference, but these alternatives will save us some typing,make it easier to reuse code in our tests, and keep our tests a little biteasier to read. And if I’m just mocking a method that returns somethingI’ll still use Moq.

Top comments (0)