|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionApplication 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 DevelopmentPeople 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. ScenarioThere 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 serves as a shield to protect us from those changes. Person EntityAccording to the client the Person must have the following attributes.
The Person entity class as shown below: 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:
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 TestsHere is our first test case that makes sure that the properties are not set if they are in invalid state. [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 “ 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 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 [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 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 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 exists 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 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 Public Address(Person person)
{
_this.Person = person;
}
This is to make sure that the 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 [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 [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 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. ConclusionIn 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!
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||