I thought it would be interesting to take a closer look at the Test Driven Development support that Visual Studio 2010 Professional provides. Before diving into some code, let’s recap what we’re trying to achieve and what’s new:
What is Test Driven Development?
Test Driven Development (TDD) is a technique that supports the “test first” principle of Extreme Programming (XP) but can (and is) used independently of the development process being used. TDD is a change of approach for developers to shift from writing code, then testing it (with unit tests), to writing the tests first and then writing the code to satisfy the tests afterwards. An ideal TDD approach is to write all the unit tests for a specific feature or features, run the unit tests and get 100% failure, then start writing code to satisfy the tests until 100% pass. Why is this a good thing? There are several benefits to this:
- If you understand the requirements sufficiently to start coding then you should be in a position write the tests for that code and therefore the set of tests represents the acceptance tests (at a unit test level) for that code.
- Having defined the set of tests you can then write the code to satisfy them. Knowing that you are looking to pass the tests really helps focus the developer’s mind on writing the minimum code to satisfy those tests. This is a Good Thing as it acts as a force against the tendency to over engineer the code (additional methods and/or signatures, functionality that no-one specifically asked for but “might be useful” etc..) and potentially add both complexity and bugs. It’s tidy; once the test passes I must, by definition, have written sufficient code and I should therefore stop.
This is quite a change in approach and, of course, relies on the tests being sufficient and comprehensive enough for the above benefits to be valid.
What’s new in Visual Studio 2010?
The practical “problem” with TDD is that unit tests are code calling code, and therefore for the unit tests to even compile, let alone run, you need the target code to be called within the unit tests to exist in some form. That’s a bit of killer if you’re trying to follow the TDD approach as that now implies that I must have my target code before I can write my unit tests – exactly the opposite to the TDD approach!
Visual Studio 2010 provides a pragmatic solution this by allowing you to write test cases without the target code existing. This is achieved by generating the stubs for that target code as you develop the test. This allows you to keep your focus entirely on the tests whilst providing sufficient generated code to allow you to write and run the tests. Best of all, it’s very simple.
Let’s walkthrough a simple scenario. I want to implement a Shopping Cart class in C# using a TDD approach. First of all I simply create a new project (in this case I created a C# Class Library but it could be anything). The project is called MyShop and just to prove the point I’m going to delete the default Class1.cs that gets created, so we now have a solution that looks like this:
In other words, it’s just an empty project. Let’s now add a unit test file (Test menu, New Test) called ShoppingCartTest:
I’ve let this create a new Test Project (MyShopTests) as I like to keep my unit tests and code separate. The solution now looks like this:
So we only have one class in the entire solution – ShoppingCartTest.cs. Let’s add a test method into that class.
Notice the squiggly red lines under ShoppingCart – why? Because the class doesn’t exist, I’ve just typed it in and it’s telling me that it can’t reference it. This where the new capabilities appear. Hover the mouse over the line or press Control . and you’ll see this option:
Generate class for ‘ShoppingCart’ will create a new class for me with default settings. I want to Generate new type though as I want to make sure that I create the class in the code project, not in the test project:
I’m happy with all the other defaults – create a public class, in a file called ShoppingCart.cs, but I want to specify the Project as MyShop. Select that project and click OK. If you now look at the Solution Explorer you can see the class now exists:
Note that the MyShopTests project now has a reference to the MyShop project, and a using statement has also been added to the test case. The squiggly lines disappear from the test method as there is now a class and a reference to it. Next we can start to code the test steps themselves. What I expect to test is a method that allows me to add an item to the cart, and to verify that it’s been successfully added. To keep it simple let’s assume we pass in an Item and get a boolean returned that indicates if it was successfully added (i.e. there was sufficient stock and it’s been reserved). I’d first like to create an Item that we’ll pass into the new method:
Simply generate Item in the MyShop project as we did the ShoppingCart. I think Item is better suited as a Struct so let’s create that rather than a class:
I then want to set the value of two fields on the Item; itemId and quantity. These don’t exist yet either so repeat but this time choose between properties and fields:
Now we call the method we want to test, AddItem. Of course AddItem doesn’t exist, so let’s Control . on AddItem to generate the method:
There’s only one option, which is what I want, so let’s take it :-). The last step is to actually call Assert comparing the expected and actual results:
We’re all done – we’ve written a unit test, and we are now able to run it, although we haven’t written a line of the implementation code ourselves. So let’s run it:
It failed! What went wrong? Nothing – this is exactly what we want. Remember that we haven’t written any implementation code yet, so I really don’t want my tests showing as passing. Double-click on the Test Result to take us to the generated stub for the implementation:
There are two things to point out here. First, the method returns a NotImplementedException so that I don’t accidentally think that the code is complete. I need to remove that when I'm ready. Second, notice that the generated method signature correctly picks up the argument type (an Item) and the correct return type (a bool). This is also true in the Item struct:
I never explicitly declared these fields as type int, but it’s been inferred from the values I set in the test method.
Repeat, Rinse, Lather
This pattern can now be continued ad infinitum – write a test, generate the required code, run the test(s). You can either keep going with the tests and then return to the implementation or write a test and then concentrate on that test’s implementation. I’d prefer the first route as it allows you to stay in “test” mode and I find that once I write a couple of tests other scenarios come to me and this approach then allows those to be captured as I think of them.
I hope this provides a useful overview of how you can start to apply TDD techniques and take a much more test centric approach to development with the support of Visual Studio 2010.