Sunday, August 14, 2005

Make your design testable - Test driven design

Make your design testable - Test driven design


Everyday we hear great deal of words like "test driven development", "unit tests", "test-first programming" etc.. I know every developer loves if they can cover 100% of their code with unit tests. But most of the time, the complain is that they do not have enough time for writing unit tests.

In my view the root cause to the problem is not actually the time, but the design of our components. Most of the developers cant find enough time to write tests for their components, just because it is almost impossible to write test cases with the component design. Most of the time, the way our component is integrated with its environment makes it impossible to simulate the dependencies on a test environment.

Next obvious question is "How do we write our components to be testing friendly". One good answer to the question is "Use Dependency Injection". Two of the most popular injection mechanisms are "Constructor Injection" and "Setter Injection". Unlike their big names the concepts are pretty simple :) . Let me take an example.. Let's take a situation which is generally considered fairly difficult to write test cases, i.s testing UI workflows....

In our example the business flow will be as follows;
-- fetch all customers from the database via a DAO
-- show customer list to the user on a GUI View
-- user will pickup a customer from the list
-- send a billing mail to the customer using MailSender service


Our simple workflow will be dependent on a DAO, a GUI View and aMail sender service for it's operations. The business workflow under test will look like the following:


class BusinessProcess {

private ICustomerDAO dao;
private IView view;
private IMailSender
mailSender;
........
.....
public void perform() {

Customer[] customers = dao.getAllCustomers(); //get all customers from db

view.setInput(customers); //ask ui view to display customerlist

view.open(); //this method is blocked until user selects a customer

Customer selectedCustomer = view.getSelected(); //get user selected customer

mailSender.sendBillingMail(selectedCustomer); //send the billing mail


}

}



One important think to notice is inside the business process method we should not perform any dependency lookups. It will just use the instance variables dao, view, mailSender representing dependent services. These instance variables are initialized either by Constructor injection, or Setter Injection. In case of constructor injection, the constructor of the "BusinessProcess " will look like the following


class BusinessProcess{

private ICustomerDAO dao;
private IView view;
private IMailSender
mailSender;

public BusinessProcess( ICustomerDAO dao, IView view, IMailSender mailSender) {

this.dao = dao;
this.view = view;
this.mailSender = mailSender;

}

}





Next important point is that our BusinessProcess is not dependent on any Implementation of the services. It just dependent on the interfaces ICustomerDAO, IView and IMailSender. This will allow you to write mock services and inject those mock services to the business process inside your test environment.

If you take the above design it is easy to write a test which will simulate complete business process even including user interactions. U will need to write mock objects for ICustomerDAO, IView and IMailSender services and inject those to the BusinessProcess at your test case.

Probably by now you got the basics of a testable design. Once you get your design correct then it will be a fun writing tests to your component. Always when you design think about the testability. It will not only make your components testable but will improve the extensibility of your component. Also this will be a good starting point for a test-first development practice :) ....

3 comments:

Hasith Yaggahavita said...

Sorry I forgot to mention.. I thought about writing this post after listning to this presentation done by Kent Beck. This is a presentation every developer must listen to...

Hasith Yaggahavita said...

Here is a good begginer's guide to dependency Injection.

88Pro said...

Thnaks hasith for the links