This is part two of a series:
- What is Test Driven Development
- Test Driven Development: In Practice
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.
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?