TDD

Test Driven Development: In Practice

This is part two of a series:


Previously I talked about the basics of Test Driven Development or TDD, as well as some of the rules around the practice. While it's important to get the theory, lets face facts, it can be a little dry. So in this post I want to take you though some practical TDD.

The Scope

I'm going to take you through developing a simple message service for an app. As the app could be online or offline the service will have to meet the following acceptance criteria.

  • If there is a network connection then send the message directly
  • If there is no network connection then put the message on the message queue
  • If sending the message directly then make sure the message queue has been cleared first

The Setup

I will be coding this using Visual Studio 2017 for Mac with XUnit and Moq. While there may be some syntax differences between testing frameworks the techniques will be the same.

For those of you unfamiliar with Moq. It is a library for creating mock objects in .NET. By using Moq I don't have to create hard-coded test classes myself. I've found doing this soon leads to test suites which are hard to maintain, difficult to understand and hard to change. If you haven't tried using it in your tests, I highly recommend it.

Let's get cracking!

Solution Setup

To start I'm going to create a new solution, then add a class library and a unit test project. While I'm here I'll add a project reference between the unit test project and the class library. It should look something like this.

solution-setup

The First Test

As I wrote in the What Is Test Driven Development post.

Tests should be documentation for what the code does

Looking at the first acceptance criteria, if there is a network connection then send the message directly. I'm going to need a mechanism for checking the state of the network connection. This certainly isn't the responsibility of the message service, this should be handled by a dedicated class. So I'm going to delegate this work to a NetworkInformation class which will expose the state of the connection.

I'm just going to create a new test class called MessageServiceTests. I prefer to append "Tests" to the end of the name of the class I'm testing. It will hold the tests for the MessageService class. Then add the following code.

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
}

Red Phase

I'm now in a red phase, there is no such thing as an INetworkInformation.

When I identify something I see as an external responsibility. I will mock it out in this way and fill in the detail later. It means I can get on and write my class, without having to dive off and build the NetworkInformation class first. More importantly, it's good design. My Message Service is depending on an abstraction from that start. And I can design that abstraction before having to worry about creating its concrete implementation.

Now I will write just enough code to get back to a green phase.

In the PracticalTDD.Application project. I'm going to create a folder called Network, then a new interface inside called INetworkInformation. To finish I'll add this using statement to the test class.

using PracticalTDD.Application.Network;

I should now be back to green phase with no compile issues. On with the test...

Green Phase

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();    
}

Red Phase

I need a mechanism for sending directly. I tend to prefer wrapping the HttpClient, so I've gone with an IHttpProvider which will do just that.

Much the same as above I don't want this class to have to worry about how the message will get sent. So I'm putting that responsibility behind an interface and moving on.

In the application project I'll create a Http folder with a IHttpProvider inside it. Then add the following using statement to the test class.

using PracticalTDD.Application.Http;

Green Phase

Next I'm going to setup my mock NetworkInfomation class. I need something to call in order to check if there is a network connection. I think a property called HasConnection will work nicely. This will return a boolean indicating the current state of the connection.

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);
}

Red Phase

As I want to send via HTTP when there is a network connection. I have set the mock to return true from the HasConnection property. But of course, I don't have that property as I only created a empty interface.

I'll switch over to the INetworkInformation interface and create the property.

bool HasConnection { get; }

I should now be able to compile again.

Green Phase

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);
    
    var message = new Message();
}

Red Phase

Obviously I need a message to send. For now I'll create a blank class called Message in the root of the Application project. Then add the using statement.

using PracticalTDD.Application;

Green Phase

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);
    
    var message = new Message();
    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object);
}

Red Phase

This should be the last part of the arrange step. I now need to create the actual message service class.

In the Application project, I'll add a new class called MessageService with the following code.

readonly IHttpProvider httpProvider;
readonly INetworkInformation networkInformation;

public MessageService(INetworkInformation networkInformation, IHttpProvider httpProvider)
{
    this.networkInformation = networkInformation;
    this.httpProvider = httpProvider;
}

Green Phase

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);
    
    var message = new Message();
    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object);
    
    // Act
    messageService.Send(message);
}

Red Phase

I haven't created a Send method yet on the MessageService class, I'll add that in now.

public void Send(Message message)
{    
}

Green Phase

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);
    
    var message = new Message();
    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object);
    
    // Act
    messageService.Send(message);
    
    // Assert
    mockHttpProvider.Verify(x => x.Send(message), Times.Once);
}

Red Phase

I now need to add the Send method to the IHttpProvider interface.

void Send(Message message);

Green Phase

I should now have the first test completed and compiling.

I'll now run the test and make sure it fails. Which will put me back into a red phase.

Red Phase

Now I need to write the code to make the test pass. So I'm going to check if there is a connection and if there is I'm going to call send on the HttpProvider.

if (_networkInformation.HasConnection)
    _httpProvider.Send(message);

Green Phase

I now have a passing test!

It may seem like I've done a lot for what is essentially a few lines of production code. But I've already been able to make a couple of important design decisions. I now have a interface for checking the network state. I also have an interface to manage HTTP calls. With these pieces now in place I can start to write more test code before hitting a red phase.

Most importantly though, I'm starting with something that fails which I then make pass. For me this adds a lot of security to what I'm developing.

Let's carry on with the next test.

Refactor

I don't feel there is anything worth refactoring at this point in time. I'm only one test in and I don't have any duplication yet. So I'm going to move onto the next test.

The Second Test

This test will cover the second acceptance criteria, If there is no network connection, then put the message on the message queue. My plan is to pass the message to a MessageQueue when there is no network connection.

public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);
    
    var message = new Message();
    
    var mockMessageQueue = new Mock<IMessageQueue>();
}

Red Phase

I have a compile error, I need to create the IMessageQueue interface. I'm going to add a new folder to the application project called MessageQueue and create a IMessageQueue interface inside.

As I mentioned in part one of this series. In practice, it is not always best to rigidly stick to the 3 Laws. In the last test I did. But from here on I'm going to be a bit less rigid.

So, while I'm here I'm going to add the following method to IMessageQueue.

void Add(Message message);

Then go back to the test and add the following using statement.

using PracticalTDD.Application.MessageQueue;

Green Phase

public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    
    mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);
    
    var message = new Message();
    
    var mockMessageQueue = new Mock<IMessageQueue>();
    
    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object, mockMessageQueue.Object);
}

Red Phase

I now need to alter the MessageService class to take an IMessageQueue in its constructor.

readonly IMessageQueue _messageQueue;

public MessageService(INetworkInformation networkInformation, IHttpProvider httpProvider, IMessageQueue messageQueue)
{
    _networkInformation = networkInformation;
    _httpProvider = httpProvider;
    _messageQueue = messageQueue;
}

I've changed the signature of the constructor and by doing so I've broken the first test. I need to update that test to have a mockMessageQueue and to pass that to the MessageService constructor.

public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable() 
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();
    var mockMessageQueue = new Mock<IMessageQueue>();
    
    networkInformation.Setup(x => x.HasConnection).Returns(true);
    
    var message = new Message();
    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object, mockMessageQueue.Object);
    
    // Act
    messageService.Send(message);
    
    // Assert
    mockHttpProvider.Verify(x => x.Send(message), Times.Once);
}

Green Phase

public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable()
{
    // Arrange
    var mockNetworkInformation = new Mock<INetworkInformation>();
    var mockHttpProvider = new Mock<IHttpProvider>();

    mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);

    var message = new Message();

    var mockMessageQueue = new Mock<IMessageQueue>();

    var messageService = new MessageService(mockNetworkInformation.Object, mockHttpProvider.Object, mockMessageQueue.Object);

    // Act
    messageService.Send(message);

    // Assert
    mockMessageQueue.Verify(x => x.Add(message), Times.Once);
}

With those changes I should now be able to compile and run both tests...

The first test is passing once again, the second is failing as expected.

Red Phase

In the MessageService I just need to add an else to the if statement. Then call the Add method on the _messageQueue.

if (_networkInformation.HasConnection)
    _httpProvider.Send(message);
else
    _messageQueue.Add(message);

Green Phase

I now have both tests passing. Also did you notice how much quicker I got through the second test?

Refactor

I have started to duplicate some code. I have multiple places where I am initialising the INetworkInformation and IHttpProvider mock objects. The same goes for the Message class and the MessageService class.

With xUnit a new instance of the test class is created for every test. So I can put common setup code into the constructor. Also, if I implement the IDisposable interface on the test class, I can add any clean up code there. If you are using NUnit for your testing there are the [SetUp] and [TearDown] attributes. You can place these on methods in your test class which will then be called before and after each test which will achieve the same result.

Mocks

I'm going to add three private fields to the test class for each of the mocks. Then I'm going to instantiate each of them in the constructor. Finally I'm going to null each instance in the Dispose method, possibly a bit unnecessary but I like to be safe.

private Mock<INetworkInformation> _mockNetworkInformation;
private Mock<IHttpProvider> _mockHttpProvider;
private Mock<IMessageQueue> _mockMessageQueue;

public MessageServiceTests()
{
    _mockNetworkInformation = new Mock<INetworkInformation>();
    _mockHttpProvider = new Mock<IHttpProvider>();
    _mockMessageQueue = new Mock<IMessageQueue>();
}

public void Dispose()
{
    _mockNetworkInformation = null;
    _mockHttpProvider = null;
    _mockMessageQueue = null;
}

The next step is to refactor all the methods to use the new class fields instead of the old method instances.

[Fact]
public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable()
{
    // Arrange
    _mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);

    var message = new Message();

    var messageService = new MessageService(_mockNetworkInformation.Object, _mockHttpProvider.Object, _mockMessageQueue.Object);

    // Act
    messageService.Send(message);

    // Assert
    _mockHttpProvider.Verify(x => x.Send(message), Times.Once);
}

[Fact]
public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable()
{
    // Arrange
    _mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);

    var message = new Message();

    var messageService = new MessageService(_mockNetworkInformation.Object, _mockHttpProvider.Object, _mockMessageQueue.Object);

    // Act
    messageService.Send(message);

    // Assert
    _mockMessageQueue.Verify(x => x.Add(message), Times.Once);
}

I'm now going to run my tests to make sure that everything is still green after my refactor...

All green.

Message instances

There are several repetitions of the Message class being instantiated. I'm going do exactly the same as I did for the mocks I've just cleaned up.

private Message _message;

public MessageServiceTests()
{
    _mockNetworkInformation = new Mock<INetworkInformation>();
    _mockHttpProvider = new Mock<IHttpProvider>();
    _mockMessageQueue = new Mock<IMessageQueue>();

    _message = new Message();
}

[Fact]
public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable()
{
    // Arrange
    _mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);

    var messageService = new MessageService(_mockNetworkInformation.Object, _mockHttpProvider.Object, _mockMessageQueue.Object);

    // Act
    messageService.Send(_message);

    // Assert
    _mockHttpProvider.Verify(x => x.Send(_message), Times.Once);
}

[Fact]
public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable()
{
    // Arrange
    _mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);

    var messageService = new MessageService(_mockNetworkInformation.Object, _mockHttpProvider.Object, _mockMessageQueue.Object);

    // Act
    messageService.Send(_message);

    // Assert
    _mockMessageQueue.Verify(x => x.Add(_message), Times.Once);
}

As before, I'm now going to run all my tests to check that everything is still working...

All green.

Message Service instances

The creation of the MessageService is repeated in every test as well. This is the last piece of duplication that I want to factor out. Again, I will just repeat what I did for the mocks and the message class above.

public class MessageServiceTests : IDisposable
{
    private MessageService _messageService;
    private Mock<INetworkInformation> _mockNetworkInformation;
    private Mock<IHttpProvider> _mockHttpProvider;
    private Mock<IMessageQueue> _mockMessageQueue;
    private Message _message;

    public MessageServiceTests()
    {
        _mockNetworkInformation = new Mock<INetworkInformation>();
        _mockHttpProvider = new Mock<IHttpProvider>();
        _mockMessageQueue = new Mock<IMessageQueue>();

        _message = new Message();

        _messageService = new MessageService(_mockNetworkInformation.Object, _mockHttpProvider.Object, _mockMessageQueue.Object);
    }

    [Fact]
    public void SendsMessageViaHttp_When_NetworkConnectionIsAvailable()
    {
        // Arrange
        _mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);

        // Act
        _messageService.Send(_message);

        // Assert
        _mockHttpProvider.Verify(x => x.Send(_message), Times.Once);
    }

    [Fact]
    public void SendsMessageToQueue_When_NetworkConnectionIsNotAvailable()
    {
        // Arrange
        _mockNetworkInformation.Setup(x => x.HasConnection).Returns(false);

        // Act
        _messageService.Send(_message);

        // Assert
        _mockMessageQueue.Verify(x => x.Add(_message), Times.Once);
    }

    public void Dispose()
    {
        _mockNetworkInformation = null;
        _mockHttpProvider = null;
        _mockMessageQueue = null;
        _messageService = null;
    }
}

Now to run the tests...

All green.

Now that looks a lot better. The test class looks very clean and much more maintainable. The refactor step is so important in TDD. Once you have something that works always go back and try and make it better.

The Third Test

Finally, I'll cover the third acceptance criteria. If sending the message directly then make sure the message queue has been cleared first. This is going to be pretty simple. I need to call a method that will send all messages currently held on the queue. Before I call the Send method on the HttpProvider.

public void ClearsMessageQueueBeforeSendingViaHttp_When_NetworkConnectionIsAvailable()
    {
        // Arrange
        var sendAllMessagesCallTime = DateTime.MinValue;
        var sendCallTime = DateTime.MinValue;

        _mockNetworkInformation.Setup(x => x.HasConnection).Returns(true);

        _mockMessageQueue.Setup(x => x.SendAllMessages())
                                      .Callback(() => {
                                          sendAllMessagesCallTime = DateTime.Now;
                                      });

        _mockHttpProvider.Setup(x => x.Send(_message))
                                     .Callback(() => {
                                         sendCallTime = DateTime.Now;
                                     });

        // Act
        _messageService.Send(_message);

        // Assert
        _mockMessageQueue.Verify(x => x.SendAllMessages(), Times.Once);
        _mockHttpProvider.Verify(x => x.Send(_message), Times.Once);
        Assert.True(sendCallTime > sendAllMessagesCallTime);
    }

In order to check what was called first I have used the callback feature in Moq to set the time the method was called. Then I've compared them to check that the SendAllMessages method was called first. To be honest I'm not over the moon about this code. While it does prove one was called before the other, it just doesn't feel a great way to do it. But at this point in time I don't know of a better way to test for something like this. So if anyone can suggest a better way then please let me know in the comments.

Red Phase

I now have a couple of compile errors. The SendAllMessages method doesn't exist. So I'll add that to the interface now.

void SendAllMessages();

I can run the tests again and now I have a failing test. To get this test to pass I'll alter the Send method on the message service.

public void Send(Message message)
{
    if (_networkInformation.HasConnection)
    {
        _messageQueue.SendAllMessages();
        _httpProvider.Send(message);
    }
    else
        _messageQueue.Add(message);
}

Green Phase

After running the tests I'm looking at all green.

Refactor

With the refactor I did after test two, I don't feel there is anything much to improve in the test class. The message service itself is really simple so there isn't much refactoring to do there either. So at this point I'm going to declare my MessageService complete.

Wrapping Up

In this post I have gone through a practical example of TDD, albeit an extremely simple example. This has been a very challenging post to write but I hope you will find something useful in here.

Once again I want to reiterate the importance of the refactor step. In my experience far to many developers don't treat their test suites with the same care they do their production code. But it's so important to do this. That way you keep your test suites maintainable, which means you're more likely to keep writing tests.

When tests become a burden they stop being run, then stop being written.

I'm still learning every day with TDD. So if you have any improvements or suggestions then please let me know in the comments below.

Do you have any methods which make TDD more effective?