Styles

Sunday, August 29, 2010

Benefits of Unit Testing

Writing unit tests is an important part of writing software, but I rarely see someone take a balanced view of them.  I've seen developers shun them because of the perception that they don't provide enough value for the effort necessary to make them, and I've seen developers who act as if the presence of unit tests in a solution automatically makes good software.  Neither of these are true.  They are worth writing, but it is important to realize their benefits and drawbacks.

How unit tests work

Unit tests are essentially bits of code written and run for the sole purpose of testing components of production software.  The idea is that it is no longer necessary to run the entire application to test the functionality of these components.  For example, assume that we're writing software that takes applications for a program, and if the application is received less than 30 days before the start of the program, then an extra $100 is applied to the program fee. Our unit tests (in pseudocode) might look like this:

Function AppliesBeforeDeadline
DateTime today = "8/15/2010"
DateTime startDate = "10/1/2010"
Assert.IsFalse(CheckLateFeeNeeded(today, startDate))
End Function

Function AppliesAfterDeadline
DateTime today = "8/15/2010"
DateTime startDate = "9/1/2010"
Assert.IsTrue(CheckLateFeeNeeded(today, startDate))
End Function

Function AppliesOnDeadline
DateTime today = "8/1/2010"
DateTime startDate = "8/30/2010"
Assert.IsTrue(CheckLateFeeNeeded(today, startDate))
End Function

Function AppliesRightBeforeDeadline
DateTime today = "8/1/2010"
DateTime startDate = "8/31/2010"
Assert.IsFalse(CheckLateFeeNeeded(today, startDate))
End Function

There are four tests here.  The first, "AppliesBeforeDeadline", tests to make sure that when we apply prior to 30 days before the deadline, the fee is not applied.  "AppliesAfterDeadline" tests for the opposite scenario in which we are applying after the 30 days prior to the deadline.  There are also two tests that check for boundary conditions; meaning the the last two tests test scenarios close to where the functionality should change.

The real benefit here is that now that we've tested these scenarios, we don't have to do nearly as much testing with the application is running.  We can test these normal circumstances by running a simple function, which is less time-consuming compared to starting the entire application.  Perhaps the best benefit is that we can use these tests as regression tests at a later date.  If someone wanted to change the way "CheckLateFeeNeeded" works at some point, it would be nice to know that he/she didn't break anything in the process of making those changes.

The major drawback I see to unit tests is that developers will often assume that because a particular bit of code has unit tests written against it, it doesn't need to be tested in the application as a whole.  To see why this assumption is dangerous, let's take a closer look at our unit tests. In this scenario, we didn't even test for all of the types of inputs we could reasonably expect, much less test for exceptional conditions.  Some further tests might need are:

  • What happens when one of the dates is in February, a month with only 28 days?  
  • If one of the dates is in UTC, is the function able to correctly calculate the difference in days?  
  • Will the function still work if the days are 375 days apart (does the function incorporate years into its calculation)?  
  • Will the function still work if one date is in December and the other is in January?  

Even if we write tests and all these pass, we also must consider scenarios where one of the dates is missing or null and/or the start date is after the application date.  Just because we have unit tests doesn't mean the function works in all scenarios.

Unit tests also can't guarantee that the function is always called correctly.  A developer could easily switch the two dates and enter the start date where the application date goes and vice versa.  If the developer (or project manager, supervisor, or stakeholder) assumes that because a unit test exists the application must be functioning well he/she will be in for some unpleasant surprises when the user starts applying for programs.

The bottom line is that unit tests can be very helpful if used properly.  Regardless of whether they are used properly, they can give you a false sense of security that your application has fewer bugs than it does.  It is important to realize their benefits and limitations before deciding how much to use them in your project.

No comments:

Post a Comment