Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | Videos | Photos | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » Enterprise Development » C# .NET 2.0 Test Driven Development

C# .NET 2.0 Test Driven Development

This article will demonstrate test driven development by stepping through the test driven development cycle using a small sample project.

Author Rank:
Total page views :  58257
Total downloads :  994
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
TestDriven.Calculator.zip
 
Become a Sponsor


Overview

There are many benefits of test driven development including better end product with well defined supporting unit tests and having a programming paradigm that is a bit more flexible in regards to scope changes.  Having unit tests available will make our code more maintainable over the long run is invaluable because we can modify our code without fear such as when we are adding/modifying functionality or refactoring.  It could even be considered crucial when we have projects where the scope is likely to change throughout the development lifecycle like in the case where the functional specifications are not clearly defined before we begin producing code.

Test driven development can be a huge shift in the development approach for many of us.  The test driven approach basically chips away at the solution like a sculptor would at a marble block instead of trying to define and create a monolithic application in one shot.

As a first step, we'll define the interfaces out software will adhere to.  The interface should clearly define how our class is to interact with other classes and what information it will expose.  This is a crucial step in any software design process because we really have to know where we are going and the interface serves as a map that will help us get there.

After we have an interface, we'll begin with the test-driven development cycle. 

  1. Add a test
  2. Run all tests and watch the new one fail (and the rest succeed)
  3. Modify our code to make the test succeed
  4. Run all tests and watch them succeed
  5. Refactor, if necessary
  6. Run tests to ensure everything still works
  7. Repeat

Like I said, this will be a totally new approach to coding for some of us, but well worth it.  Like they say: "Don't knock it till you try it." 

Part I. Getting Started

For this demonstration, we'll be building a simple calculator that will add and subtract a double and display the information.  We will also need to be able to clear the calculator's memory. 

Personally, I like to separate the interfaces into a separate solution because once our interfaces are "baked" (they are not going to be changing), we can make a reference to the interfaces in new projects and code against them without having to pull in any concrete implementation.  As a matter of fact, we could completely re-write the implementation on the back end if we needed to without breaking other solutions if all projects agree on a core set of interfaces to code against.

So we'll start with one solution and two projects.  One project will be for the interfaces (I like using "Core" in the namespace) and the other for our implementation.

Testdrivendevelopment1.gif

Next, we need to define our interface for the adding calculator and put it in our core project.

public interface ICalculator

{

    double Total { get; }

    double Add(double value);

    double Subtract(double value);

    double Clear();

}

Testdrivendevelopment2.gif

Then we'll set up the implementation project by first adding a reference to the TestDriven.Calculator.Core project and creating an empty Calculator class which will implement our interface.

using System;

using System.Collections.Generic;

using System.Text;

using TestDriven.Calculator.Core;

 

namespace TestDriven.Calculator

{

    public class Calculator:ICalculator

    {

    }

}

VisualStudio 2005 has a great feature which you probably already know about, but I thought I'd mention it anyways.  It allows us to stub out the properties and methods required for implementing interfaces.  If we right-click on the interface name (ICalculator) and select "Implement Interface > Implement Interface" we get the following code.

using System;

using System.Collections.Generic;

using System.Text;

using TestDriven.Calculator.Core;

 

namespace TestDriven.Calculator

{

    public class Calculator:ICalculator

    {

        #region ICalculator Members

 

        public double Total

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

 

        public double Add(double value)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public double Subtract(double value)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        public double Clear()

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        #endregion

    }

}

So we have our interface and test project set up our solution should look like this:

Testdrivendevelopment3.gif
 
Now for the unit tests. For certain versions of Visual Studio unit testing will be built in, but just in case you didn't want to spend the cash to get one these versions, we'll be implementing unit tests in this sample project using NUnit.  The process will be pretty much the same either tool you use.  If you don't have a unit testing framework installed on your machine, go to
http://nunit.com and download and install the most recent release.

We'll create a new project called TestDriven.Calculator.UnitTests

Testdrivendevelopment4.gif

In our new project, we'll make an empty class called "CalculatorTest.cs" and we'll need references to our other two projects and the nunit.framework.

Testdrivendevelopment5.gif

Now we are ready to get started developing our solution.

Part II. Test Driven Development

First we need to decorate our test class so NUnit knows that it is for testing.

using System;

using System.Collections.Generic;

using System.Text;

using NUnit.Framework;

using TestDriven.Calculator.Core;

using TestDriven.Calculator;

 

namespace TestDriven.Calculator.UnitTests

{

    [TestFixture]

    public class CalculatorTest

    {

 

    }

}

On a side note, if we needed to do some preliminary test setup, we could define a method and decorate it with "[Setup]" as follows.

[TestFixture]

public class CalculatorTest

{

    /// <summary>

    /// Used for pre-test preperations

    /// </summary>

    [SetUp]

    public void Init()

    {

 

    }

 

}

First Iteration in Development Cycle

First things first, let's create a method to test the calculator constructor.

/// <summary>

/// Testing construction of calculator

/// </summary>

[Test]

public void CalculatorConstructorTest()

{

    ICalculator calc = new Calculator();

 

    Assert.IsTrue(null != calc, "Construction failed");

    Assert.IsTrue(0.0D == calc.Total, "Value should initially be 0.0");

}

There are a couple of things I'd like to point out.  First, our test method is decorated with "[Test]" so our testing framework knows it is a test case.  You'll notice we are using the ICalculator interface to hold a reference to our calculator.

ICalculator calc = new Calculator();

It is important that we code to the interface wherever possible.  This enables us to change core functionality without breaking how our unit tests interact with the underlying classes.  The only place we'll be referencing the concrete Calculator class is when we have to use the constructor. 

Next, we'll write our assertions. These are the things that the unit test will be asserting for us: We need to make sure the constructor worked and returns an object implementing the correct interface.  We will also make sure the initial value of the calculator is 0.0.

Assert.IsTrue(null != calc, "Construction failed");

Assert.IsTrue(0.0D == calc.Total, "Value should initially be 0.0");

Next, we'll have to complete a couple of steps to test.

  1. Compile our solution. 
  2. Create a new NUnit project (from the NUnit program)
  3. Add our TestDriven.Calculator.CalculatorTests assembly to the Nunit project (from the NUnit program)

Now we can run our tests from the NUnit program and see what we get:

Testdrivendevelopment6.gif

I know what you are thinking now: "Red lights mean failure -- Dude, this is Awesome!" (Actually, all joking aside, this is exactly what we want.)

This is the first step in the process.  We made a test that failed.  Next step is to change our code so that the test passes.  In order to do this, we'll build a constructor for the Calculator class and implement the Total property.

public class Calculator:ICalculator

{

 

    public Calculator()

    {

        m_total = 0.0;

    }

 

    private double m_total;

 

    #region ICalculator Members

 

    public double Total

    {

        get { return m_total; }

    }

 

    public double Add(double value)

    {

        throw new Exception("The method or operation is not implemented.");

    }

 

    public double Subtract(double value)

    {

        throw new Exception("The method or operation is not implemented.");

    }

 

    public double Clear()

    {

        throw new Exception("The method or operation is not implemented.");

    }

 

    #endregion

}

Then recompile and run our test again.

Testdrivendevelopment7.gif
 
So now that we have passed the first test we look for places to improve the code.  There are none so we are done with the first iteration of the test driven development cycle.

Second Iteration in Development Cycle

To start, we'll build tests for the ICalculator.Add()  and ICalculator.Subtract () methods:

/// <summary>

/// Testing addition

/// </summary>

[Test]

public void CalculatorAdditionTest()

{

    ICalculator calc;

    double expected, actual;

 

    calc = new Calculator();

    expected = 1.0;

    actual = calc.Add(1.0);

 

    Assert.AreEqual(expected, actual, "0 + 1 should be 1");

    Assert.AreEqual(expected, calc.Total, "total is incorrect");

 

    expected = 2.5;

    actual = calc.Add(1.5);

    Assert.AreEqual(expected, actual, "1 + 1.5 should be 2.5");

    Assert.AreEqual(expected, calc.Total, "total is incorrect");

 

}

 

/// <summary>

/// Testing subtraction

/// </summary>

[Test]

public void CalculatorSubtractionTest()

{

    ICalculator calc;

    double expected, actual;

 

    calc = new Calculator();

    expected = -1.0;

    actual = calc.Subtract(1.0);

 

    Assert.AreEqual(expected, actual, "0 - 1 should be -1");

    Assert.AreEqual(expected, calc.Total, "total is incorrect");

 

    expected = -2.5;

    actual = calc.Subtract(1.5);

    Assert.AreEqual(expected, actual, "-1 -1.5 should be -2.5");

    Assert.AreEqual(expected, calc.Total, "total is incorrect");

}

Next, we compile and run the tests from NUnit and they will fail.

Testdrivendevelopment8.gif

Next, we'll update the Calculator class.

public class Calculator:ICalculator

{

 

    public Calculator()

    {

        m_total = 0.0;

    }

 

    private double m_total;

 

    #region ICalculator Members

 

    public double Total

    {

        get { return m_total; }

    }

 

    public double Add(double value)

    {

        m_total += value;

        return m_total;

    }

 

    public double Subtract(double value)

    {

        m_total -= value;

        return m_total;

    }

 

    public double Clear()

    {

        throw new Exception("The method or operation is not implemented.");

    }

 

    #endregion

}

Recompile and run the tests and make sure they pass

Testdrivendevelopment9.gif
 
This is the point where we would check to see if there are any places where we could refactor our code to make it more efficient.  For example, even though our current implementation is more than adequate, let's say we wanted to change the way we implemented the Subtract() method.  We will update the code, compile and run our tests to be sure we didn't break anything.

public double Subtract(double value)

{

    return Add(-1.0 * value);

}

So now we are done with our second iteration of the test driven development cycle.

Third Iteration in Development Cycle

To wrap up, we'll be working on the Clear() functionality.  Here is the test:

/// <summary>

/// Testing clearing calculator

/// </summary>

[Test]

public void ClearTest()

{

    ICalculator calc;

 

    calc = new Calculator();

    calc.Add(123.456);

    calc.Clear();

 

    Assert.AreEqual(0.0, calc.Total, "calculator has not been cleared");

}

We compile, run the tests, and see the failure

Testdrivendevelopment10.gif
 
We'll implement the method, compile and test.

public double Clear()

{

    m_total = 0.0;

    return m_total;

}

Testdrivendevelopment11.gif
 
Next, we'll look for places to refactor.  Let's change the constructor:

public Calculator()

{

    Clear();

}

We compile, test and make sure nothing is broken.

Testdrivendevelopment12.gif
 
All is well, so we are done.

Wrap up.
 
Hopefully this article gave you a basic understanding of how test driven development works.  It is a different approach than traditional development cycles and takes a bit of an adjustment to get started.  Once you try building a project using this approach, chances are you won't go back because having unit tests for your code is invaluable, especailly in large projects with complex functionality for maintenance, refactoring, and managing any types of changes that may come up.

Until next time,

Happy programming.


Login to add your contents and source code to this article
 About the author
 
Matthew Cochran
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010 Professional
Microsoft Visual Studio 2010 Professional will launch on April 12, but you can beat the rush and secure your copy today by pre-ordering at the affordable estimated retail price of $549 (US). Pre-order now.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
Developer-Ready ASP.NET 2.0 Web Hosting with 3 MONTHS FREE
Now supporting .NET 3.0 Framework with Windows Workflow Foundation, Windows Communication Foundation (WCF), Windows Presentation Foundation (WPF), windows CardSpace (WCS)! Providing more flexibility for Developers with Web Services Support and a User/Permission Manger. Also supporting MS SQL 2005/2000 with Real-Time Backups, FREE Automated Attach .MDF Tool, FREE SQL Restore and Shrink SQL DB Tools, and SQL
 
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
TestDriven.Calculator.zip
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
Great Article! by Alexander On June 20, 2007
I am amazed! The idea is really bright and explanation is absolutely clear. I like it and I will go for it definitely! Thanks!
Reply | Email | Delete | Modify | 
Thanks for this extensive tutorial by Robin On July 13, 2007
The same here! Thanks Matthew for this great article. I plan to implement unit testing in my project within the next few weeks so I quickly had to read more about it and figure out how to start. This really helped me out: article is bookmarked for future reference. :)
Reply | Email | Delete | Modify | 
How to deal with complex object ? by Advait On December 19, 2007
Hi Matthew, Nice tute. Your every tutorial has unique content. These are intermediate step for newbies. OK, let me come to the topic. Can you please post any articale on unit testing to test a class which has complex member variables like another class or an array as a member variable or abstract class etc?
Reply | Email | Delete | Modify | 
Nice Article!!! by Jagadish On August 2, 2009
Nice Article, Thanks!!!
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 2010  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.