TDD / BDD Example




Developing using TDD is more about the developer’s mindset rather than the tools and technologies that are used.    However we still need to write some actual code, and that’s where a whole range of both commercial and open source tools come into play.

There are two main tools that you will need to be able to write tests effectively:

1.      IDE (e.g. Visual Studio for C#, Eclipse for Java)

2.      Unit Test Framework (e.g. NUnit, JUnit)


Note: There is also a third tool – a Mocking framework (e.g. RhinoMocks, NMock, TypeMock) which is not a necessity, but becomes very useful if you want to achieve test isolation in integrated enterprise systems.  (This is a completely new can of worms and should be discussed at another time).

For my examples below I will use Visual Studio (C#) to write my code and NUnit as my testing framework.

Let’s choose an example to show the process you would go through of writing a test, making it pass and then refactoring your code to keep a clean design.  Although this example is trivial it should be a good introduction to how you can develop Test First.


Scenario:

An online gambling company is running a special offer where any new members that sign up using a promotion code will receive a $50 starting bonus to their account.  If they don’t use a promotion code, they will receive nothing.

A few thoughts about the system spring to mind immediately:

-          There is a customer involved
-          Customers have an account balance
-          There is a sign up process
-          You can signup with or without a promotion code
-          Signing up with a promo code means you get $50

Let’s assume the system already handles the creation of Customers, but they just cannot signup yet.  (Therefore there should already be a ‘Customer’ object and a suite of tests that describe its intended behaviour and purpose).

The solution would look something like this:



And here’s the customer class that already exists:



The ‘GamblingSignup’ project is where you would put the ‘real’ code, and the unit test code would go in the ‘GamblingSignup.Tests’ project.

So we need to be able to allow customers to signup in two different ways: With a promotion, or without – and depending on which case, a $50 bonus amount may be awarded to the customer.

So we have some simple scenarios that we need to be able to test and build into the system:

-          Allow a customer to signup without a promotion code
-          Allow a customer to signup with a promotion code and award them a $50 bonus



Let’s start by exploring the simplest case: Allow a customer to signup without a promotion code.

We would create a new file in Visual Studio so we can start writing some tests.  I’ll call the file “OnlineSignupTests.cs”.  This will be where we group all our tests involving customers that signup online.

Now that we have a file to write some tests – let’s think about the first behaviour we want to implement: “Allow a customer to signup without a promotion”

I’ll create my first “TestFixture” class inside “OnlineSignupTests.cs”. Each test fixture will represent a particular “context” in which the system is intended to be used.



So by thinking about what behaviour the system provides, we start to think about what needs to happen when a customer is signed up.

Let’s take something simple such as: The customers balance should be initialised to zero.  We can now turn that into our first test.



Notice the objects that appear in the test.  We have a Customer (which already has been built by a previous developer), but more importantly we have this “OnlineSignupService” which doesn’t actually exist yet (because we are writing Test-First).  This won’t even compile yet - so we will create the ‘OnlineSignupService’ class and ‘SignUp’ method now.



We can now compile our code, and run the tests.  Funnily enough, this first test passes:



In this case because, since the customers balance doesn’t need to change, nothing needs to occur during the signup process.  But what has been important here is that we have started to develop a ‘domain model’ that will solve the business problem. I.e. Customers now have the ability to sign up through an Online Signup service.

Now we can focus on the next scenario: Allow a customer to signup with a promotion code and award them a $50 bonus

So we create a new “TestFixture” class inside “OnlineSignupTests.cs”.



The main thing we want to check in this scenario, is that the customer is awarded a $50 bonus.



As you can see above, this test has driven new behaviour into the system.  The “SignUp” method now needs to be able to accept a promotion code.
 
We add that behaviour now:



Once again, I only write enough code to make the test compile.  Now I can run the tests again.



This time the test fails.  The customers balance isn’t increased to $50.




Now we have the task of making this test pass.  So we would write some code to do that.


We compile, and run the tests again.


Now we can be sure that our application is meeting what is required by the business.


After all that our solution now looks like this:



Now that we have a “security blanket” over our code, we can refactor it to make it simpler and remove any duplication, and re-run the tests at the end of all the changes to ensure you haven’t broken anything.

Let’s say we revisit this code in six months time, and we find that the sign up process has grown more complex.  New business requirements over that time have been introduced for ALL online signups:

-          An email must be sent to the customer.
-          Sign-up date must be recorded.
-          The customer date of birth must be validated
-          The customers first name must not contain numbers
-          The customer must be saved to the database

Each of the above new requirements should have resulted in more ‘test’ cases being written that described and tested the new behaviour.  I can open these new tests, read them to get an understanding of the intended behaviour of the system, and run them to verify everything is still working.

However the developer that did these changes whilst, still creating test first, may have created a lot of duplicate code that was having a negative impact on the system design.  We may find the code like this:




As you can see there is duplicate code in that we want to eliminate.  We could refactor it to be like this:



These changes may be risky without a suite of tests, but having them gives the developer the confidence to make changes and as a result keep the system simple and flexible.

And what’s the most important outcome of this whole process?  We can respond to the business’ needs faster and produce a higher quality product.




 del.icio.us  Stumbleupon  Technorati  Digg 

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this entry.
Comments
  • No comments exist for this entry.
Leave a comment

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.