ASP.NET Security Consultant

I'm an author, speaker, and generally a the-way-we've-always-done-it-sucks security guy who specializes in web technologies and ASP.NET.

I'm a bit of a health nut too, having lost 50 pounds in 2019 and 2020. Check out my blog for weight loss tips!

Benefits of Unit Testing

Published on: 2010-08-29

Writing unit tests is an important part of writing software. In a nutshell, they are code methods (typically written by developers) that test whether certain assumptions are true. Not only do they allow code to be tested in a quick and reliable fashion, but they also can be re-run months and years later to confirm whether the code still works as expected after changes are made. Oddly, I don't see many people taking a balanced view of them. I've seen them shunned because of the perception that they don't provide enough value for the effort necessary to make them, and I've seen them evangelized to the point where the presence of unit tests in a solution automatically were thought to make good software. Neither of these are true. Well-written unit tests are worth having, but they have limits.

How unit tests work

Before I delve into why unit tests are useful, let's go over an example of what unit tests are. In this example, let's assume that we're writing software that takes registrations from individuals for various event, and if the registration 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 (portions bolded to show the tests themselves):

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

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

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

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

There are four tests here. The first, "RegistersBeforeDeadline", tests to make sure that when we apply prior to 30 days before the deadline, the fee is not applied. "RegistersAfterDeadline" 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.

Why they're beneficial

The first benefit here is that now that we've tested these scenarios, we don't have to do nearly as much testing with the software running. We can test these normal circumstances by running a simple function, which is less time-consuming compared to starting the entire system. In other words, I, as a developer, wouldn't need to log in, create an account, create an event where the deadline just passed, just to test whether I am charged the late fee. My unit tests do that for me. The same is true for testing a registering for 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.

Unit test limitations

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?
  • What if CheckLateFeeNeeded is never called?

Our tests don't cover these scenarios, and if we skip testing like an end user entirely we will almost certainly run into problems.

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.

Conclusion

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.

This article was originally posted here and may have been edited for clarity.