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.

Monday, August 23, 2010


I came across this blog about the drawbacks of multitasking.  I have noticed that I can get more done if I simply focus on a single task at any given time.  I also find my work more enjoyable, and I usually have more energy at the end of a day with a smaller amount of multitasking than the other way around.  While I multitask occasionally, I try to avoid it whenever possible.  Then I read this rebuttal and realized that "multitasking" can mean different things to different people.  So how would I define multitasking?
  • Focus your attention on a single task at a time.  If another issue emerges that is more important, that should get 100% of your attention.  If it is not more important, then it can wait.
  • Multitasking is about the attention to a task and does not refer to the number of thoughts you must keep in your head at any given time
So avoiding multitasking doesn't mean closing off all contact with the outside world while you're working on a particular task.  You could designate certain times during the day for contact for non-urgent issues, with the understanding that you would be available by phone at other times if needed.  You could treat e-mail the same way - check e-mail periodically throughout the day, but only respond to non-critical e-mails at certain times.

Avoiding multitasking also doesn't mean that you can't put aside a particular task if you're stuck.  It means focusing 100% of your attention on whatever task you are trying to accomplish at any given moment.  If you are not making any headway, going to another task temporarily is not multitasking in my view; it's serial single-tasking.

Finally, the author of the rebuttal blog seemed to think that doing complex tasks qualifies as multitasking.  As a programmer, when I get a request for a feature change to software that I'm building, I need to consider many different ideas:
  • The most effective way to implement the solution
  • The needs of power users
  • The needs of the first-time user
  • Costs for my client
  • Unintended consequences that might arise because of my fixes
However, all of these ideas are working towards a single goal: determining how best to implement a feature requested by my client.  It is a single, multi-faceted task.  When a single task gets too complex to control, then I break it down into multiple tasks and address each one separately.  I'm still not multitasking, and by single-tasking the components I have an easier job putting the pieces together in the overall task.

So if multitasking makes one work less effectively and accomplishes nothing worth noting, then why do it on a regular basis?

Saturday, August 7, 2010

Tips for mentoring others

LHere is a great blog post that was written about mentoring new programmers, but could easily be applied to other disciplines and experienced hires too:

These tips are rather incomplete, though.  I'd like to expand on their tips:
  1. The issue with mentoring seems not to be lack of will, but lack of time.  More on this in a bit.
  2. Make sure your road map covers more than just the first few hours.  It is all well and good to show the new employee the location of the documentation, but have a plan that covers at least the first six months.  This plan should include ways to make the employee more independent as time goes on.
  3. Be tolerant of mistakes, but try to prevent common or serious ones.  Confidence building is key.
  4. Try to assign projects at first that have a training purpose as well as a practical purpose for the company.  This should be a mixture of easy projects to give the employee confidence and stretch projects to keep him or her engaged.
While most of us would agree with these tips, very often they're not followed.  Why?  The biggest reason seems to be that people usually aren't hired for a potential need, they're hired to fill a current one.  While that approach lowers the risk for the company of hiring someone who will need to be laid off, it also makes it harder for managers and experienced co-workers to get the employee up to speed in a fashion that benefits both the employee and the company.  The easiest way around this that I can see is to keep track of potential future needs (perhaps by using risk modeling tools such as these) and hire based on that.  Outsourcing and contract-to-hire positions can mitigate the risk of over-hiring, if needed.  Good mentoring is a lot easier when one feels like there is time to do it well.