Click here to Skip to main content
15,885,366 members
Articles / Web Development / ASP.NET

Designing Application Using Test Driven Development

Rate me:
Please Sign up or sign in to vote.
4.49/5 (18 votes)
1 May 2008CPOL4 min read 39.3K   206   60   6
In this article, we will take a look at designing application using test driven development.

Introduction

Application design is one of the most important aspects of creating the application. A design serves as the pillars of the application. An incorrect design can cause delays or even worse destroy the application. In this article, we will take a look at designing application using test driven development.

Evolving the Design with Test Driven Development

People often consider the process of test driven development as testing the application for different inputs. But test driven development is much more than inputs and results. Test driven development allows the developer to design the application and makes sure that the developer only writes the code that is required to produce the desired output.

The purpose of this article is to show you how the application design evolves with the help of test driven development.

Scenario

There is no actual scenario to discuss. We will start by testing two classes, Person and Address. A Person can have many Addresses. An Address must belong to a Person. As we progress, we will find out that the user stories and features keep on growing and unit tests serve as a shield to protect us from those changes.

Person Entity

According to the client, the Person must have the following attributes:

  • FirstName: First name of the person
  • LastName: Last name of the person
  • MiddleName: Middle name of the person
  • DOB: Date of birth
  • DateCreated: Date and time the person entry was created
  • DateModified: Date and time the person entry was modified

The Person entity class as shown below:

C#
public class Person
    {       
        private string _firstName;
        private string _lastName;
        private DateTime _dob;
        private int _id;

 public int Id
 {
  get { return _id; }
  internal set { _id = value; }
 }           

        public string FirstName
        {
            get { return _firstName; }
            set {
              
              _firstName = value;           
            }
        }

        public string LastName
        {
            get { return _lastName; }
            set
            {
               _lastName = value;

            }
        }

        public DateTime DOB
        {
            get { return _dob; }
            set {

               _dob = value;                           
            }
        }     
    }

What to Test?

One common problem that developers encounter is getting started with test driven development. How should we find out what modules to test? The best way is to study what module is extensively used in the application. Once you find the module, start writing unit tests. A good code coverage tool will also reveal important information about the usage of modules in your application.

Keeping that in mind, we picked up the phone and gave a call to our client to discuss the constraints on the application. The client explained us the following:

  1. First name cannot be left empty
  2. Last name cannot be left empty
  3. Person must be at least 18 years old

This gives us a good starting point to test our application. Let’s start by making sure that the above expectations are met.

Implementing Unit Tests

Here is our first test case that makes sure that the properties are not set if they are in invalid state.

C#
[RowTest]
      [Row("","","02/09/2000")]
      public void should_not_set_person_properties_if_in_invalid_state(
          string firstname, string lastname,DateTime dob)
      {
          Person person = new Person() { FirstName= firstname, LastName = lastname,
              DOB = dob };
          Assert.IsTrue(person.FirstName != firstname,"FirstName has been set!");
          Assert.IsTrue(person.LastName != lastname,"LastName has been set!");
          Assert.IsTrue(person.DOB != dob,"DOB has been set!");
      }

The test “should_not_set_person_properties_if_in_invalid_state” makes sure that the person properties are only set when they are in valid state. We are using the RowTest attribute provided by the MbUnit framework. The RowTest attribute enables us to provide multiple test inputs using the Row attribute. You can learn more about the attributes and the MbUnit framework by visit the Mb.unit site.

When you run the above test, it will fail. This is because we have not made any changes to our entity class Person and the values will be set. So, we need to add constraints to our Person class properties. Let’s take a look at the constraints.

C#
public class Person
    {       
        private string _firstName;
        private string _lastName;
        private DateTime _dob;

        public const int LEGAL_AGE = 18;             

        public string FirstName
        {
            get { return _firstName; }
            set {
              
                if(!String.IsNullOrEmpty(value.Trim()))
                {
                    _firstName = value;
                }           
            }
        }

        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (!String.IsNullOrEmpty(value.Trim()))
                {
                    _lastName = value;
                }

            }
        }

        public DateTime DOB
        {
            get { return _dob; }
            set {

                // the person must be 18 or older!
                if ((DateTime.Now.Subtract(value).Days / 365) >= LEGAL_AGE)
                {
                    _dob = value;
                }                           
            }
        }   
    }

In the above code, we added several different constraints which will help us to keep the object in the valid state. Now, if you run the test, it will pass.

Now, let’s test that the Person object is saved successfully. Take a look at the test below:

C#
[Test]
        [RollBack]
        public void should_check_if_the_person_saved_successfully()
        {
            Person person = new Person() { FirstName = "Mohammad", LastName = "Azam",
                DOB= DateTime.Parse("02/09/1981") };

            person.Save();

            Assert.IsTrue(person.Id > 0);

            Person vPerson = Person.GetById(person.Id);
            Assert.AreEqual(person.FirstName, vPerson.FirstName);
            Assert.AreEqual(person.LastName, vPerson.LastName);
            Assert.AreNotEqual(person.DOB, vPerson.DOB);
        }

If you run the above test, it would fail because Person object does not expose a Save or GetById method. Let’s add those two methods:

C#
public void Save()
        {          
            // this means adding a new person
            if (this.Id <= 0)
            {
                SQLDataAccess.SavePerson(this);
            }

            else
            {
                SQLDataAccess.UpdatePerson(this);
            }
        }

        public static Person GetById(int id)
        {
            return SQLDataAccess.GetPersonById(id);
        }

The Save method uses the SQLDataAccess data access class to insert the Person in the database. The GetById method takes “id” as the parameters and returns the Person object matching the "id."

If you are in the initial phases of development and don’t want to work on the data access layer, then you can always mock it out. Mocking means you will fake out the data access layer because it does not exist yet. Once the data access layer is implemented, you can simply plug it in without changing any test code.

Let’s move on to the Address entity.

C#
public class Adress
    {
        private int _id;
        private string _street;
        private Person _person;

        public int Id
        {
            get { return _id; }
            internal set { _id = value; }
        }

        public string Street
        {
            get { return _street; }
            set { _street = value; }
        }

        public Person Person
        {
            get { return _person; }
            set { _person = value; }
        }

        public Address(Person person)
        {
            this.Person = person;
        }

The Address class contains a Person object since the Address belongs to a certain Person. One important thing to note is the Address object constructor which takes a Person object.

C#
Public Address(Person person)
{
 _this.Person = person;
}

This is to make sure that the Address must have a Person object.

This process is also known as the dependency injection. There are two types of dependency injection constructor and property. In the above example, we used the constructor dependency injection.

And here is a test to make sure that the Address must have a Person object or Address must belong to a Person.

C#
[Test]
        public void should_make_sure_that_an_address_must_have_a_person()
        {
            Address address = new Address(new Person());
            Assert.IsNotNull(address.Person);
        }

Simple right!

Now, let’s try to add an Address to a Person object. Here is the test.

C#
[Test]
        public void should_be_able_to_add_an_address_to_the_person()
        {
            Person person = new Person() { FirstName = "Mohammad", LastName = "Azam" };

            Address address = new Address(person);
            person.AddAddress(address);

            Assert.AreEqual(1, person.Addresses.Count());
        }

If you run the above test, it will fail because the Person object does not have the AddAddress method and the Addresses property. So, let’s add those two things.

C#
private IList<Address> _addresses = new List<Address>();

  public IList<Address> Addresses
        {
            get { return new ReadOnlyCollection<Address>(_addresses); }
        }

        public void AddAddress(Address address)
        {
            address.Person = this;
            _addresses.Add(address);           
        }

Now if you run the test, it will pass.

Conclusion

In this article, we learned how to get started with test driven development and how the TDD approach helps us to design better applications. In the next article, we are going to see how our design changes when we encounter failing tests.

I hope you liked the article — happy coding!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
My name is Mohammad Azam and I have been developing iOS applications since 2010. I have worked as a lead mobile developer for VALIC, AIG, Schlumberger, Baker Hughes, Blinds.com and The Home Depot. I have also published tons of my own apps to the App Store and even got featured by Apple for my app, Vegetable Tree. I highly recommend that you check out my portfolio. At present I am working as a lead instructor at DigitalCrafts.




I also have a lot of Udemy courses which you can check out at the following link:
Mohammad Azam Udemy Courses

Comments and Discussions

 
GeneralThank you Pin
peterkmx1-May-08 23:20
professionalpeterkmx1-May-08 23:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.