In this post, I am going to show you the thought process and steps that I take while doing TDD. I am going to focus on a simple scenario. This will show you the baby steps that I take while writing my unit tests. This is going to be a two-part tutorial. In this first part, I am going to show you how to follow the 3 step process of TDD i.e. Red, Green & Refactor. I am going to write a few basic tests for a Shopping Cart. In part two I will add further tests that require me to use mocking. During this tutorial, I will be thinking out loud. So you can understand how I tempt the urge to jump ahead at times and stick to what TDD defines.
I want to write code for the following functionality in my cart.
- Add an item to cart
- Remove an Item from the cart
- Add an item multiple times to the cart
- Handle adding an Item that is out of stock
Open up Visual Studio and create a new blank solution. Now add a class library and name it “ShoppingCart”. Next, let’s add a project for our unit tests. Add a new “Unit Test Project” and name it “ShoppingCart.Tests”. Our production code is going to reside in the “ShoppingCart” project. Whereas all the tests are going to be in the ShoppingCart.Tests project. The first test that I want to write is for adding an item to an empty cart. Create a class “CartTests” in the test project and add the following code to it.
Notice the name of my test, your unit tests should ideally communicate three things to its reader.
- The name of the method that you are testing
- The context in which you are executing your unit test
- The expected outcome of the test.
Providing this information in your unit test makes it very clear for a future developer who will read these tests. If I try to build the code now, it will fail. I need to create Cart and CartItem classes in my project. I am using Resharper which makes it a breeze to refactor code. It can generate both the classes and their required method and properties. Here are my generated classes.
Now I can build my solution but if I run my test it fails. I have achieved the Red part of the TDD cycle, my test is failing. Now I need to make my test pass and I will only write enough code to make it pass. How about this?
Voilà my test passes! At this point, you might start wondering if I am insane. Remember the most important rule of TDD is that you let your tests drive your code. You only write enough code to make our tests pass. In this case, all I need to make the test pass is to return 1 from my Count property. Notice I now have a blank Add method. I just changed the Count property to a read-only Property that returns 1. I know that this code is not right. In order to prove it wrong, I have to write a failing test 🙂
So let’s continue.
As you can see the expected result was 1 but the actual value of the total was 0. Let’s write code to make the test pass. Remember I have to write the simplest code possible to make both the test pass.
Again I used the same method to make my test pass, just return hard-coded value. This makes both our tests pass. Now I know that this code is incorrect but I will have to make a test prove it first. Now let’s see if I need to refactor this code? Remember TDD is a 3 step iteration process Red, Green & Refactor. I have first made sure that my tests fail, then I have only written enough code to make them pass and after writing each code I need to see if I need to do any refactoring.
One practice that I use when I write unit tests is to create a factory class or separate method for the creation of classes. You can already see that almost every test we write for the Cart class, we will need to create an instance of it. What happens if we have written 100 tests and then in the future, we need to modify the constructor? If you have not separated out the creation of cart object in a separate method or class, you will end up having to change all the tests that you have written which is a maintenance overhead. I am just going to extract out the creation of cart class in a separate method for now which might seem completely unnecessary at the moment but I know it is extremely useful. So let’s refactor the code and create a method for creation of the Cart object.
Now I will add a test to verify that if I add two items to an empty cart, its count should be 2.
Now let’s make this test pass.
You can see how this test has forced me to write the correct implementation for the Cart class. I can no longer continue to make all my tests pass without correcting the logic inside my Cart class. The Total property, however, is still returning a hard-coded count but you can guess that the next test will force me to make it right!. At this point, my code is pretty clean and I don’t think we need to do any refactoring so let’s write the next test.
Now let’s will fix the code.
Here is the complete code that I have up to this point.
The important thing to learn when getting started with TDD is that you have to strictly follow the rules of TDD and do not need to jump ahead to write extra code. When you know that your code is incorrect, just write a failing test to prove it and then make the test pass. Continuously refactor your code and try to separate out the logic for creation of objects as you might need to change it later at some stage. TDD can be a bit challenging for beginners but you only need some time to get the hang of it and then you will start seeing the benefits that it has to offer. In the next tutorial, I will continue to add to this code and implement the rest of the tests. Till then Happy Coding!
[…] the previous post, I gave an overview of how to get started with TDD. I tried to highlight the thought process and […]
This is extremely useful. Thanks a bunch