There are a lot of good tutorials on how to start Test driven development (TDD) that help understanding how he can start using this software development methodology. Unfortunately most of those tutorials explain how to write simple unit test to test some imaginary class and do not explain using mocking frameworks as part of the tutorial.
Many newcomers to the Test Driven development world do not learn about mocking framework mainly because of the misperception that mocking is difficult to use and an unnecessary complication of tests.
Hopefully in the following post I would be able to dispel this notion.
The background story
You are a developer working as part of a team developing a new CRM system.
Today you are tasked with adding new functionality to an Order object – When a ProductId is added to a newly created Order the TotalPrice property of that order shall be equal to the product’s Price.
The only problem is that the developer that is responsible of the Product object didn’t finish writing the data access code yet.
Note that even if the product object was fully implemented it’s we don’t want to create a new database every time we run our tests.
So what is TDD?
If you haven’t heard of TDD before I suggest you go and read the definition of Test Driven Development on Wikipedia:
Test-driven development (TDD) is a software development technique that uses short development iterations based on pre-written test cases that define desired improvements or new functions. Each iteration produces code necessary to pass that iteration’s tests. Finally, the programmer or team refactors the code to accommodate changes. A key TDD concept is that preparing tests before coding facilitates rapid feedback changes. Note that test-driven development is a software design method, not merely a method of testing.
Test-Driven Development is related to the test-first programming concepts of Extreme Programming, begun in 1999,[1] but more recently is creating more general interest in its own right.[2]
Programmers also apply the concept to improving and debugging legacy code developed with older techniques.[3]
TDD can be summarized as a set of the following actions:
- Write a test that fail
- Write code to make the test pass
- Make sure that all of the previous tests still pass
- Refactor your code
- Repeat (if necessary)
[Image from Yet Another Language Geek blog – Musings on Software Testing]
How to start – writing the test
In order to start TDD we’ll need the following:
- Editor to write your code – most windows developer prefer to use Visual Studio but feel free to use whichever editor/IDE you usually use
- Unit testing framework – you can go and download NUnit it’s free and easy to use.
- For the purpose of this tutorial you will also need Typemock Isolator – you can get a 30-day FREE evaluation copy here.
Create a new class library (dll) project and add a reference to nunit.framework.dll.
And now we can write our first test:
1 |
<span style="color: #000000;"><span class="lnum"> 1: </span>[TestFixture] </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> OrderTests </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 3: </span>{ </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 4: </span> [Test] </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> AddProduct_AddOneProductToOrder_OrderTotalCostEqualProductPrice() </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 6: </span> { </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 7: </span> var productPrice = 15.00; </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 8: </span> var order = <span class="kwrd">new</span> Order(); </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 9: </span> order.AddProduct("product1");</span> |
1 |
<span style="color: #000000;"><span class="lnum"> 10: </span> Assert.AreEqual(productPrice, order.TotalCost);</span> |
1 |
<span style="color: #000000;"><span class="lnum"> 11: </span> } </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 12: </span> } </span> |
Running the test above cause an exception to be thrown from line 9. because product data access is not implement yet running Product.Retrive throws an unimplemented exception.
Now we have to decide what to do:
- Complain that the Product class was not finished yet.
- Use Fake product instead.
Faking It – how to write your first mocked object
- Download Typemock Isolator and install it on your computer.
- Add references to Typemock.dll & Typemock.ArrageActAssert.dll
- read below
In this test we want to Fake the static call Product.Retrive so that it would return a product. Using Isolate.WhenCalled we can define that behavior
1 |
<span style="color: #000000;"><span class="lnum"> 1: </span>[Test] </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">void</span> AddProduct_AddOneProductToOrder_OrderTotalCostEqualProductPrice() </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 3: </span>{ </span> |
1 |
<span style="color: #000000;"><strong><span class="lnum"> 4: </span> </strong>var productPrice = 15.00;<strong>var p = <span class="kwrd">new</span> Product(<span class="str">"product1"</span>);</strong> </span> |
1 |
<span style="color: #000000;"><strong><span class="lnum"> 5: </span></strong> </span> |
1 |
<span style="color: #000000;"><span class="lnum"> <span style="font-weight: bold;">6: </span></span><span style="font-weight: bold;"> var p = new Product("product1"); </span></span> |
1 |
<span style="color: #000000;"><span class="lnum"> <span style="font-weight: bold;">7: </span></span><span style="font-weight: bold;"> p.Price = productPrice; </span></span> |
1 |
<span style="color: #000000;"><span class="lnum" style="font-weight: bold;"> 8: </span><strong style="font-weight: bold;"><strong><span style="font-weight: normal;">Isolate.WhenCalled(() => Product.Retrieve(</span><span class="kwrd" style="font-weight: normal;">string</span><span style="font-weight: normal;">.Empty)).WillReturn(p);</span></strong></strong> </span> |
1 |
<span class="lnum" style="color: #000000;"> 9: </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 10: </span> var order = new Order(); </span> |
1 |
<span class="lnum" style="color: #000000;"> 11: </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 12: </span> order.AddProduct("product1"); </span> |
1 |
<span class="lnum" style="color: #000000;"> 13: </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 14: </span> Assert.AreEqual(productPrice, order.TotalCost); </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 15: </span> }</span> |
On lines 6-8 we create a new Product and tell Isolator to return it when Retrieve method being called.
Back to TDD
When running the test another exception is thrown – because we haven’t implemented AddProduct functionality yet.
So we write a simple implementation to fix our test:
1 |
<span style="color: #000000;"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> Order </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 2: </span>{ </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">double</span> TotalCost { get; set; } </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> AddProduct(string productId)</span> |
1 |
<span style="color: #000000;"><span class="lnum"> 5: </span> { </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 6: </span> var product = Product.Retrieve(productId);</span> |
1 |
<span style="color: #000000;"><span class="lnum"> 7: </span> TotalCost += product.Price; </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 8: </span> } </span> |
1 |
<span style="color: #000000;"><span class="lnum"> 9: </span>}</span> |
Note that AddProduct currently only increase the amount of the order’s total cost, because TDD means that you write only the code required to pass the test. In the future we’ll add tests to check that the product was added to our Order as well.
Now our test finally pass. Congratulation not only do you know how to write unit tests you have witnesses faking of objects as well.
Not too hard now is it?
Want to start unit testing? Take advantage of our 30-day FREE trial of Isolator Complete. Get it now.