Featured Post

The great debacle of healthcare.gov

This is the first time in history when the president of the United States of America, or probably for any head of state around the world,...

Showing posts with label Unit testing. Show all posts
Showing posts with label Unit testing. Show all posts

Monday, May 26, 2008

Why development of Unit Test classes should go in parallel along with development

The question that always strikes me about the unit testing is, why people across the board give less priority on writing unit testing code over functional testing when it comes to software quality. I have been in numerous number of software development project and in most of the cases I found that trend, specially when we needed to do a trade off to hit deadline, the first thing gets under the guillotine was the unit testing. Some cases there are pure ignorance on the importance of the unit testing and in other cases where the importance of the unit testing was acknowledged, the unit testing didn't end up in a good coverage (I consider 80% to 90% as a good coverage) of the production code.

I had seen two types of practices in my projects when it comes to create unit test classes. First practice is to set aside the unit testing during the software development phase and once we've a running application in production, we put some time on code quality and maintainability. This can be defined as "After the fact Unit Testing". The management starts pushing the development team to write unit test classes to get 100% code coverage or at least close to 100%. And the second practice, let's call that as "Unit Testing in Parallel", which is kind of not so popular, is to take unit testing as part of the development from the very beginning and force developers to deliver test classes along with production code. In projects, where the unit testing is deemed as necessary but discarded for the sake of project strict deadline, they tend to follow the first practice mentioned above. I'm not considering the Test Driven Development (TDD) practices in this discussion as I believe that's too extreme and extremely rare in the current software industry.

Let's take a look into the "After the fact Unit Testing" approach. It's never gonna happen that you would be able to get a good coverage of unit test classes and even you get some, the quality of unit test code would be horrible. The reason is that you would write to get the coverage or fulfill the management needs not the developers need. There are other downside that I would discuss soon.

In the "Unit Testing in Parallel" approach: This should be the ideal approach when you start a project development. There is one downside, unit test takes extra effort on top of production code that the project management team or customer don't want to effort it initially until they start getting high number of defects production when a simple change in requirement is accepted by the project at the end of the game.

I would advocate for the second approach and explain why this is much much superior to any other approach with the way out of all short falls of this approach.

When a developer starts developing a method, he definitely has few test cases in his mind to validate his implementation. The test case can be either developed in mind before the implementation of the method has started or some time at the end of the development to check if the developed method works as expected. Lets consider the development of the below production code development:

public double convertCurrency(double foreignCurrencyAmt, double exRate) {

return baseAmt / exRate;

}

When the above method is being developed, the developer tests the code by passing sample data and validate against expected return value. Most of the time this testing is done verbally in case it straight forward like the above logic but the complex logic is tested by running the application or by some other means.

Suppose the developer validates his logic by passing foreignCurrencyAmt as 700 and exRate as 70 and expecting the converted currency amount as 10. Either he would test it inside his mind or by running the application with above mentioned data set. So, we can divide the development into two parts - logic development and logic validation. Hypothetically we can distribute 70% time to logic development and 30% time to logic validation of the entire development time of production code.

In first approach where unit testing is done after the development is finished, when the developer would do the development of unit test cases, he would have to spend at least same amount of time (i.e. 30% time) to reconstruct the validation logic. Practically this time would be more because he would need to recall the production code logic again (unless the code is very simple like the earlier example code). My point is here, this 30% time to construct the validation logic (input data set and expected return values) would be duplicated both in the development of production code and development of unit test code. And this effort duplication can be avoided if the production code and unit test code are developed in parallel (pseudo parallel is the most appropriate phrase). There are some overhead of writing formal unit testing code that takes, for example, X amount of time, that we can't avoid and would definitely impact the project time line. I don't want to stress more on the advantages of spending this overhead time in this blog as there is no debate on this. I just want to end my discussion here with this statement that we're able to save the development time (the hypothetical 30% time that is duplicated to construct validation logic) if we develop unit testing code in parallel with production code.

Saturday, March 1, 2008

Unit Testing of DAO classes

Data Access Objects (DAO) are important components of any software system. Ideally the DAO classes shouldn't contain any logic other than few Data access logic. In that context, the DAO classes are less error prone while changing code. But there are several reasons, as described below, those forces us to write Unit test for DAO classes.
  1. In practice any code can breaks regardless of complex or simple
  2. Achieve 100% code coverage by unit testing
  3. Testing of data access logic whatsoever is written
Let me share one bitter experience from one of my project that motivated me to write unit testing for DAO classes in my next projects. A senior developer had put a seemingly harmless debugging code in a DAO method at mid night, day before the first working presentation to the Board of Directors, that prints the retrieved record's id from the database. The method was used by a business logic component that generates the next account number in the system and start over if the system has no existing accounts. The code definitely was tested by the developer before he delivered it to the version control but not by any Unit Test code rather manual functional test. He had not covered the scenario with freshly installed database because it takes a lot of hassles that includes execution of both 'remove and create' schema scripts i.e he had to access the database in some way moving away from the IDE. Anyway, the convinced developer left the office at mid night unknowingly injecting a disastrous statement that almost caused the project to be closed in the next day.

Our happy Engineering Manager had clicked the first link of application next day to create an account in the freshly installed system (we intentionally had kept the system fresh to show them how it will start from the very beginning when it would be deployed first) that caused the system to crash, with a NullPointerException trace printed on the browser, in front of the Board members who had come to see the already delayed project's first working presentation. Do I need to detail the situation in the demonstration room after that incident? I don't think so.

To consider unit testing for DAO classes we have to take the implementation technique based on the underlying technology used there. If hard code JDBC is used then Mock can be applied to test the behavior whether the Connection, Statement, ResultSet etc are called and closed properly. But if Hibernate is used to DAO , as O-R mapping for example, then it doesn't make sense to use Mock as the O-R mapper (e.g Hibernate) takes care of those raw objects. In that case, the testing goal would be:
  1. Test the O-R mapping and relationship configurations (e.g hbm.xml files)
  2. Query wrote in the method (e.g using Criteria or HQL)
And the above can't be tested using mock (to achieve the isolation for unit testing) rather use of in-memory database as Test Double (ref. Martin Fowler's article). Lets see a simple implementation of Fake Test Double technique using HSQLDB's in-memory database feature.

The following class provides the Hibernate Session to the test code:

public class HibernateUtil {
private static SessionFactory factory;

public static void setSessionFactory(SessionFactory factory) {
HibernateUtil.factory = factory;
}
}


The following class creates the test schema and also helps initialize and reset the schema:

public class TestSchema {
private static final Configuration config = new Configuration();
static {
config.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect").
setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver").
setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test").
setProperty("hibernate.connection.username", "sa").
setProperty("hibernate.connection.password", "").
setProperty("hibernate.connection.pool_size", "1").
setProperty("hibernate.connection.autocommit", "true").
setProperty("hibernate.cache.provider_class", "org.hibernate.cache.HashtableCacheProvider").
setProperty("hibernate.hbm2ddl.auto", "create-drop").
setProperty("hibernate.show_sql", "true").
addClass(Customer.class).
addClass(Account.class);
}

public static void reset() throws SchemaException {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
try {
session.createQuery("Delete Customer").executeUpdate();
session.createQuery("Delete Account").executeUpdate();
} catch (HibernateException e) {
tx.rollback();
throw new SchemaException(e.getMessage());
}
finally {
tx.commit();
session.close();
}
}

public static void initialize() {
HibernateUtil.setSessionFactory(config.buildSessionFactory());
}

The actual Unite test class:

public class HibernateAccountDaoTest extends TestCase {

CustomerDao custDao;
AccountDao accountDao

public static void main(String[] args) {
junit.swingui.TestRunner.run(HibernateDaoTest.class);
}

public void setUp() {
TestSchema.initialize();
custDao = new HibernateCustomerDao();
custDao.setSession(HibernateUtil.getSession());

accountDao = new HibernateAccountDa0();
accountDao.setSession(HibernateUtil.getSession());
}

public void tearDown() {
try {
TestSchema.reset();
} catch (SchemaException e) {
e.printStackTrace();
}
}

public void testInsertAccount() {
// prepare data set
Customer customer = new Customer(1, "Name", "Address");
Account account = new Account(customer);
accountDao.insertAccount(account);

Account queriedAccount = dao.findAccountById(1);
assertNotNull(account.getBalance()));
}
}



Resources:

http://martinfowler.com/articles/mocksArentStubs.html - Truly speaking, I'd started liking mock object by reading this article
http://www.theserverside.com/tt/articles/article.tss?l=UnitTesting - I just followed this article's way
http://www.javaranch.com/journal/2003/12/UnitTestingDatabaseCode.html - The traditional approach of database code unit testing

Thursday, February 28, 2008

A Comparative analysis of Unit Testing in isolation

What I understand about Unit Testing can be expressed using this simple statement "Unit testing is all about the testing in isolation". And that makes the Unit Testing unique from the other form of testing like Integration Testing, Functional Testing or System Testing. I don't see any debate about the goal of Unit testing but when it comes to implementation of the testing and how to isolates the System Under Test (I followed Martin Fowlers naming conventions here) there are lots of differences in opinion and implementation technics. Lets start to talk about this by showing the components of a unit test-

  1. Unit test class, usually written in JUnit framework
  2. System Under Test (SUT), the targeted class that I'm gonna unit test and
  3. Collaborators classes, SUT interacts with those classes to accomplish it's goal

To implement the Unit testing in isolation, today I'll consider three popular approaches and will try to show the comparative advantages and disadvantages from a developer point of view. Those three popular approaches to achieve the isolation are:

  1. Point cut approach
  2. Stub approach
  3. Mock approach

Point cut approach uses the Aspect Oriented Programming (AOP) technique to implement the isolation. The principal behind this is, point cut the collaborator method calls from SUT and eventually return the expected outcome from the method. Usually an Advice class is written that point-cuts the method calls. The advantages of this approach are:

  • The SUT is unaware about the point cut and can be configured externally.
  • No mock or stub objects need to be written. etc.

The disadvantages are:

  • The test data (expected outcome) are stored not in the test class itself.
  • Becomes complicated when the point-cut advice needs to provide different response for different test cases.
  • The developer needs to be aware of multiple classes and files incase use xml as configuration for point-cut
  • Not possible to verify the behavior of the calls

Stub approach stub out the collaborator by injecting the Stubbed collaborators and hence isolate the SUT from having the real collaborators method calls to be invoked. The advantages of this approach are:

  • It isolates the SUT from each collaborators replacing with Stub versions hence makes it easy to understand and maintain
  • Don't bother about how the method has been implemented i.e. overlooks the implementation detail and looks for the outcome (it is known as state verification)

The disadvantages are:

  • It doesn't perform the behavior verifications

Mock approach is more like Stub approach and uses mock objects to isolate the SUT from the collaborators by injecting the Mock collaborator objects. The hallmark difference of Mock approach from the stub approach is that the Mock approach considers the behavior test as well. So the mock approach has all the benefits of Stub approach has and has also the ability to perform the behavior verifications though some people see this one as demerits.

Now see a simple testing implementation in Mock approach using a popular Mock Testing Framework, EasyMock.


public class SampleServiceTest extends TestCase {
private SampleDao daoMock;
private MockControl daoControl;

public static void main(String[] args) {
junit.swingui.TestRunner.run(SampleServiceTest.class);
}

public void setUp() {
daoControl = MockControl.createControl(SampleDao.class);
daoControl.setDefaultMatcher(MockControl.EQUALS_MATCHER);
daoMock = (SampleDao)daoControl.getMock();
}

public void testDeleteSampleData() {
SampleDataId dataId = new SampleDataId(1);
SampleData data = new SampleData(dataId);
SampleDataAudit audit = new SampleDataAudit(sampleData);

daoMock.findData(dataId);
daoControl.setReturnValue(data);
daoMock.evict(data);
daoControl.setVoidCallable();
daoMock.deleteSampleData(dataId);
daoControl.setVoidCallable();
daoMock.insertSampleDataAudit(audit);
daoControl.setVoidCallable();
daoControl.replay();

SampleService csi = new SampleService();
csi.setSampleDao(daoMock);
csi.deleteSampleData(dataId);
daoControl.verify();
}
}