This is an archive of blog posts from a "long time ago". If you find this useful, let me know and I'll revive it.

code blog foo - tag line bar

Introduction to Behavior Driven Development and Mocking in .Net

Warning

This is a long and dense post that will take a lot of concentration to get through. Also, this is entirely my opinion on what I consider to be "effective" unit testing, so take the time to digest what is written and make your own evaluations (think critically).

Setting the Stage

I don't want to waste your time, because I know it's valuable. Also, if you have any questions or need any clarifications, please don't hesitate to email me (ar@amirrajan.net). So before you start reading, make sure:

I really don't like trivial examples. So again, this is pretty dense code that is actually being used in one of my web sites (borrowedgames.com). So take the time to really take it in. Let's start drinking from the fire hose shall we?

IMessageManager

In my web site, I have added messaging capabilities (you can send and receive internal messages from a user...very similar to what you would see in Facebook or LinkedIn). Here is the interface specification for my "MessageManager".

public interface IMessageManager
{
    void SendMessage(Guid senderUserId, 
        Guid recipientUserId, 
        string subject, 
        string body, 
        DateTime sendDate);
    void MarkMessageAsRead(Guid ownerUserId, Guid messageId);
    void DeleteInboxMessage(Guid ownerUserId, Guid messageId);
    void DeleteOutboxMessage(Guid ownerUserId, Guid messageId);
}

MessageManager : IMessageManager

And this is the concrete implementation of the IMessageManager. We'll go through this code line by line so we understand what we will be testing.

public class MessageManager : IMessageManager
{
    private IUserInboxSaver _userInboxSaver;
    private IUserOutboxSaver _userOutboxSaver;
    private IUserInboxRetriever _userInboxRetriever;
    private IUserOutboxRetriever _userOutboxRetriever;

    public MessageManager(IUserInboxSaver userInboxSaver, 
        IUserOutboxSaver userOutboxSaver, 
        IUserInboxRetriever userInboxRetriever, 
        IUserOutboxRetriever userOutboxRetriever)
    {
        _userInboxSaver = userInboxSaver;
        _userOutboxSaver = userOutboxSaver;
        _userInboxRetriever = userInboxRetriever;
        _userOutboxRetriever = userOutboxRetriever;
    }

    void IMessageManager.SendMessage(Guid senderUserId, 
        Guid recipientUserId, 
        string subject, 
        string body, 
        DateTime sendDate)
    {
        _userInboxSaver.SaveInboxMessage(Guid.NewGuid(), 
            senderUserId, 
            recipientUserId, 
            subject, 
            body, 
            true, 
            sendDate);

        _userOutboxSaver.SaveOutboxMessage(Guid.NewGuid(), 
            senderUserId, 
            recipientUserId, 
            subject, 
            body, 
            sendDate);
    }

    void IMessageManager.MarkMessageAsRead(Guid ownerUserId, Guid messageId)
    {
        if(_userInboxRetriever.InboxMessageExists(ownerUserId, messageId))
        {
            _userInboxSaver.SetIsNew(messageId, false);
        }
    }

    void IMessageManager.DeleteInboxMessage(Guid ownerUserId, Guid messageId)
    {
        if(_userInboxRetriever.InboxMessageExists(ownerUserId, messageId))
        {
            _userInboxSaver.DeleteInboxMessage(messageId);
        }
    }

    void IMessageManager.DeleteOutboxMessage(Guid ownerUserId, Guid messageId)
    {
        if (_userOutboxRetriever.OutboxMessageExists(ownerUserId, messageId))
        {
            _userOutboxSaver.DeleteOutboxMessage(messageId);
        }
    }
}

MessageManager Dependencies

Notice that the concrete implementation of MessageManager depends on the following repository/database interfaces: IUserInboxSaver, IUserOutboxSaver, IUserInboxRetriever, IUserOutboxRetriever. Here are the interface definitions.

public interface IUserInboxSaver
{
    void SaveInboxMessage(Guid messageId, 
        Guid senderUserId, 
        Guid recipientUserId, 
        string subject, 
        string body, 
        bool isNew, 
        DateTime sendDate);
    void SetIsNew(Guid messageId, bool isNew);
    void DeleteInboxMessage(Guid messageId);
}

public interface IUserOutboxSaver
{
    void SaveOutboxMessage(Guid messageId, 
        Guid senderUserId, 
        Guid recipientUserId, 
        string subject, 
        string body, 
        DateTime sendDate);
    void DeleteOutboxMessage(Guid messageId);
}

public interface IUserInboxRetriever
{
    bool InboxMessageExists(Guid ownerUserId, Guid messageId);
    List<IMessage> GetNewInboxMessages(Guid ownerUserId);
    List<IMessage> GetReadInboxMessages(Guid ownerUserId);
    IMessageWithBody GetInboxMessage(Guid ownerUserId, Guid messageId);
}

public interface IUserOutboxRetriever
{
    bool OutboxMessageExists(Guid ownerUserId, Guid messageId);
    List<IMessage> GetOutboxMessages(Guid ownerUserId);
    IMessageWithBody GetOutboxMessage(Guid ownerUserId, Guid messageId);
}

Line by Line

So far so good? Alright, time for the line by line. Let's start with the constructor.

    private IUserInboxSaver _userInboxSaver;
    private IUserOutboxSaver _userOutboxSaver;
    private IUserInboxRetriever _userInboxRetriever;
    private IUserOutboxRetriever _userOutboxRetriever;

    public MessageManager(IUserInboxSaver userInboxSaver, 
        IUserOutboxSaver userOutboxSaver, 
        IUserInboxRetriever userInboxRetriever, 
        IUserOutboxRetriever userOutboxRetriever)
    {
        _userInboxSaver = userInboxSaver;
        _userOutboxSaver = userOutboxSaver;
        _userInboxRetriever = userInboxRetriever;
        _userOutboxRetriever = userOutboxRetriever;
    }

Pretty straight forward constructor. MessageManager depends on a few repository interfaces to store messages in the database. Each user has an Inbox and an Outbox....and the retrievers and savers facilitate the persistence and retrieval of the messages. Now for the SendMessage() method.

    void IMessageManager.SendMessage(Guid senderUserId, 
        Guid recipientUserId, 
        string subject, 
        string body, 
        DateTime sendDate)
    {
        _userInboxSaver.SaveInboxMessage(Guid.NewGuid(),     //messageId
            senderUserId,                                    //senderId
            recipientUserId,                                 //recipientId
            subject,                                         //subject
            body,                                            //body
            true,                                            //isNew
            sendDate);                                       //datesent

        _userOutboxSaver.SaveOutboxMessage(Guid.NewGuid(),   //messageId
            senderUserId,                                    //senderId
            recipientUserId,                                 //recipientId
            subject,                                         //subject
            body,                                            //body
            sendDate);                                       //sendDate
    }

The SendMessage() method, adds the message entry in the sender's outbox and inserts the same message in the recipients inbox. Also, the inbox message is marked as a new message. Since Guid.NewGuid() is passed in for the messageId, a message would get inserted in both the sender's outbox and the recipient's inbox (the messageId is a primary key in the database). Now for the MarkMessageAsRead() method.

void IMessageManager.MarkMessageAsRead(Guid ownerUserId, Guid messageId)
{
    if(_userInboxRetriever.InboxMessageExists(ownerUserId, messageId))
    {
        _userInboxSaver.SetIsNew(messageId, false);
    }
}

Given the owner and the message id, the repository is queried to see if the message exists. If it does then the message's IsNew property is set to false using _userInboxSaver.SetIsNew(). The method would go to the database, find the message and set the IsNew column to 0 for that particular message. Now for DeleteInboxMessage() and DeleteOutboxMessage().

void IMessageManager.DeleteInboxMessage(Guid ownerUserId, Guid messageId)
{
    if(_userInboxRetriever.InboxMessageExists(ownerUserId, messageId))
    {
        _userInboxSaver.DeleteInboxMessage(messageId);
    }
}

void IMessageManager.DeleteOutboxMessage(Guid ownerUserId, Guid messageId)
{
    if (_userOutboxRetriever.OutboxMessageExists(ownerUserId, messageId))
    {
        _userOutboxSaver.DeleteOutboxMessage(messageId);
    }
}

Each method first queries the repository to ensure that the message exists for the owner and the message id. If the combination exists then the respective delete method is called. A delete sql statement would get generated and executed inside of the DeleteInboxMessage() and DeleteOutboxMessage() methods.

The Unit Tests (BDD Style)

Whew...that was a lot of explanation (make sure you understand what MessageManager is doing before reading on)...now that the stage is set, let's look at how to test this class.

Naming

The key to naming your tests is to make them as human readable as possible. It is 100% okay to have class names that are really long (and method names that are even longer). You want these unit tests to document the behavior of the code so that any developer can jump in, look at the unit tests, and have a pretty good understanding of what you are trying to do.

The .cs Files and the Methods

You'll have many .cs files to fully test one class. In the case of MessageManager, I had to create four .cs files. Each .cs file has behavioral style methods....you'll probably have a .cs file for each method in a class (use your best judgement on this...start with one .cs file...the very moment you see your test initialization getting too complicated, start breaking it up). Here are the names of the .cs files and the method names:

Time to Test...and Mock

Let's look at the scaffolding for when_sending_message.cs.

[TestClass]
public when_sending_message
{
    private IMessageManager _messageManager; //this is the class we will test
    public when_sending_message()
    {
        //_messageManager initialization will go here
    }

    [TestMethod]
    public void should_insert_message_into_senders_outbox()
    {

    }

    [TestMethod]
    public void should_insert_message_into_recipients_inbox()
    {

    }
}

After creating the scaffold, we need to initialize _messageManager in the constructor so we can test it. If you recall, the concrete implementation of MessageManager has a few dependencies: IUserInboxSaver, IUserOutboxSaver, IUserInboxRetriever, IUserOutboxRetriever. We do not want to test MessageManager's dependencies. So that means we cannot (and should not) use the concrete implementations of these interfaces. We have to use mocks to orchestrate what the concrete implementation of these repository classes would have done.

Manually Making Your Mocks

I used to manually create my mocks. It was fairly easy and didn't require me to spend time on learning a mocking framework...I don't any more, but I'll let you make that decision for yourself. I'll show how to both manually mock your dependencies and how to mock using a mocking framework (I use Moq....it's awesome).

So the problem....I need to new up a MessageManager, and provide mocks for all the interfaces that this class needs. You can accomplish this by having your unit test class implement each interface MessageManager needs. Take a look.

[TestClass]
public when_sending_message :
       IUserInboxSaver, 
       IUserOutboxSaver, 
       IUserInboxRetriever, 
       IUserOutboxRetriever
{
    private IMessageManager _messageManager; //this is the class we will test
    public when_sending_message()
    {
        //all the interfaces have been implemented 
        //and either do nothing or return a default value.
        //now that when_sending_message implements 
        //all the interfaces needed by MessageManager,
        //I can now new it up.

        //"this" (being the unit test class) is passed in 
        //for each parameter of messagemanager
        _messageManager = new MessageManager(this, this, this, this);  
     }

    [TestMethod]
    public void should_insert_message_into_recipients_inbox()
    {

    }

    [TestMethod]
    public void should_insert_message_into_senders_outbox()
    {

    }

    #region IUserInboxSaver Members

    void IUserInboxSaver.SaveInboxMessage(Guid messageId, Guid senderUserId, Guid recipientUserId, string subject, string body, bool isNew, DateTime sendDate)
    {

    }

    void IUserInboxSaver.SetIsNew(Guid messageId, bool isNew)
    {

    }

    void IUserInboxSaver.DeleteInboxMessage(Guid messageId)
    {

    }

    #endregion

    #region IUserOutboxSaver Members

    void IUserOutboxSaver.SaveOutboxMessage(Guid messageId, Guid senderUserId, Guid recipientUserId, string subject, string body, DateTime sendDate)
    {
 
    }

    void IUserOutboxSaver.DeleteOutboxMessage(Guid messageId)
    {

    }

    #endregion

    #region IUserInboxRetriever Members

    bool IUserInboxRetriever.InboxMessageExists(Guid ownerUserId, Guid messageId)
    {
        return false;
    }

    List<IMessage> IUserInboxRetriever.GetNewInboxMessages(Guid ownerUserId)
    {
        return null;
    }

    List<IMessage> IUserInboxRetriever.GetReadInboxMessages(Guid ownerUserId)
    {
        return null;
    }

    IMessageWithBody IUserInboxRetriever.GetInboxMessage(Guid ownerUserId, Guid messageId)
    {
        return null;
    }

    #endregion

    #region IUserOutboxRetriever Members

    bool IUserOutboxRetriever.OutboxMessageExists(Guid ownerUserId, Guid messageId)
    {
        return false;        
    }

    List<IMessage> IUserOutboxRetriever.GetOutboxMessages(Guid ownerUserId)
    {
        return null;
    }

    IMessageWithBody IUserOutboxRetriever.GetOutboxMessage(Guid ownerUserId, Guid messageId)
    {
        return null;        
    }

    #endregion
}

Alright. We've newed up MessageManager...now let's flush out the should_insert_message_
into_recipients_inbox() method.

[TestMethod]
public void should_insert_message_into_recipients_inbox()
{
    Guid senderId = Guid.NewGuid();
    Guid recipient = Guid.NewGuid();

    _messageManager.SendMessage(senderId, recipientId, "subject", "body", DateTime.Today);

    //make sure a new guid was generated for the message id
    Assert.AreNotEqual(Guid.Empty, _lastInboxMessageId);

    //make sure that rest of the parameters match up as expected
    Assert.AreEqual(senderId, _lastInboxSenderUserId);
    Assert.AreEqual(recipientUserId, _lastInboxRecipientUserId);
    Assert.AreEqual("subject", _lastInboxSubject);
    Assert.AreEqual("body", _lastInboxBody);
    Assert.AreEqual(DateTime.Today, _lastInboxSendDate);

    //ensure that the inbox message is marked as new
    Assert.AreEqual(true, _lastInboxIsNew);
}

[...]

private _lastInboxMessageId;
private _lastInboxSenderUserId;
private _lastInboxRecipientUserId;
private _lastInboxSubject;
private _lastInboxBody;
private _lastInboxIsNew;
private _lastInboxSendDate;

void IUserInboxSaver.SaveInboxMessage(Guid messageId, Guid senderUserId, Guid recipientUserId, string subject, string body, bool isNew, DateTime sendDate)
{
   //since we injected this unit test class
   //as the IUserInboxSaver, this method will
   //be called any time MessageManager calls SaveInboxMessage.
   //we need to record what was given as the parameters so we
   //can do our assertions.  This method essentially
   //orchestrates the insertion of the message into the database.

   _lastInboxMessageId = messageId;
   _lastInboxSenderUserId = senderUserId;
   _lastInboxRecipientUserId = recipientUserId;
   _lastInboxSubject = subject;
   _lastInboxBody = body;
   _lastInboxIsNew = isNew;
   _lastInboxSendDate = sendDate;
}

The should_insert_message_into_senders_outbox() method would be tested in the same fashion. The mocked method would record the parameters that it was given and the unit test would perform assertions on those parameters.

If you are thinking this is a lot of work to effectively unit test...well then you're right. It just comes down to how much you value stable code (and how much it will cost if you introduce a bug into a production environment). Unit tests are necessary. Period. They are an essential part of software engineering (and we all see software as a science/craft). Thankfully, mocking frameworks (like Moq) will make your life a lot easier.

Making Your Mocks With Moq

Here is how you would new up the MessageManager class using [Moq].

using Moq;

[TestClass]
public class when_sending_message
{
    private IMessageManager _messageManager;
    private Mock<IUserInboxSaver> _userInboxSaver;
    private Mock<IUserOutboxSaver> _userOutboxSaver;
    private Mock<IUserInboxRetriever> _userInboxRetriever;
    private Mock<IUserOutboxRetriever> _userOutboxRetriever;

    public when_sending_message()
    {
        _userInboxSaver = new Mock<IUserInboxSaver>();
        _userOutboxSaver = new Mock<IUserOutboxSaver>();
        _userInboxRetriever = new Mock<IUserInboxRetriever>();
        _userOutboxRetriever = new Mock<IUserOutboxRetriever>();

        _messageManager = 
            new MessageManager(_userInboxSaver.Object,
                               _userOutboxSaver.Object,
                               _userInboxRetriever.Object,
                               _userOutboxRetriever.Object);
    }

    [TestMethod]
    public void should_insert_message_into_recipients_inbox()
    {

    }

    [TestMethod]
    public void should_insert_message_into_senders_outbox()
    {

    }
}

Notice something? The unit test doesn't need to implement every interface. All you have to do is declare a Mock of each interface and inject the mock into the MessageManager class. Now for the real magic. Lets see what the unit test looks like for should_insert_message_into_
recipients_inbox().

[TestMethod]
public void should_insert_message_into_recipients_inbox()
{
    Guid senderId = Guid.NewGuid();
    Guid recipientId = Guid.NewGuid();

    _messageManager.SendMessage(senderId, recipientId, "subject", "body", DateTime.Today);

    //verify that the _userInboxSaver.SaveInboxMessage()
    //method was called 
    _userInboxSaver.Verify(
        s => s.SaveInboxMessage(It.IsAny<Guid>(), //message id can be any guid
                 senderId, //sender id
                 recipientId, //recipient id
                 "subject", //subject line
                 "body", //body of message
                 true, //should be flagged as a new message
                 DateTime.Today)); //send date should be today
}

That's it...by calling the Verify method, we can ensure that the parameters passed into SaveInboxMessage were conveyed correctly. We didn't have to manually store the values, or do any number of assertions. Moq does all of that for you. Crazy huh?

Tips

That's pretty much all I can give you to get started with creating effective unit tests. The rest comes down to practice...lots and lots of practice. Here are a few tips to help you along.


Written: 4/13/2010