Throughout my career in the software development industry, unit testing has been a practice that I have known about, somewhat understood the benefits, but never had a real opportunity to practice. When you are a part of a small development team and you are looking for ways to deliver value to your customers more quickly, skipping unit testing “feels” like you are doing the right thing. But if your project becomes more successful, more complicated, and your team grows, not having unit tests becomes risky and costly.
Unit testing is a software testing approach by which a unit of work within your source code is tested to determine if it functions properly. You can think of a unit (unit of work) as the smallest testable part of your code. A unit test is code a developer creates to test a unit. It is basically code written to test code. For example, let’s say some code has a function that accepts an array of numbers and returns the sum of the values. Several unit tests (code) can be written to test the function by providing arrays of known values as input and comparing the result to the known sum of the of the values in the input array. If the sum returned by the function matches the known sum, the unit test passes. So big deal, the function can correctly calculate the sum of the values in an input array. What’s the value in creating unit tests for that?
Well let’s expand our thinking for a moment. You have an idea that might improve the efficiency of the function by 10x and it requires that you refactor how the function sums the values of the input array. Let’s make this a little more interesting and assume you are not the original author of the function and the output of the function should be the same as before your refactor. Without unit tests in place, how do you determine if the function is still working correctly after your refactor? In this scenario is where unit testing adds tremendous value.
There are a couple of items in this scenario that contribute to increased risk and cost. All non-trivial applications/libraries will have code refactored at some point which introduces risk. Without unit testing, the only way to ensure that code continues to function correctly after refactoring is to perform manual testing and in some scenarios the code that was refactored can only be tested indirectly as a part of a larger operation. This leads to increased cost because resources (developers/testers) have to be allocated to a manual testing effort and human resources are the most expensive part of any business. In addition, this manual testing effort requires time which delays the delivery of value to the end users.
Returning to our scenario, let’s assume there were unit tests available and the tests were setup to execute as a part of the build process. After the refactor, you rebuild the code which kicks off the execution of the unit tests. Almost instantaneously you will get feedback on your code changes. Assuming that the unit tests are correctly testing the function (big assumption) and the unit tests are still passing after your refactor, you can be fairly confident that you have not introduced errors. This does not eliminate the need for all manual testing but it is safe to conclude that the manual testing efforts can be minimized and the unit testing has reduced, not eliminated, risks and cost.
Based on my view of the software development world, I would say that most development shops do not actively use unit testing as a tool to mitigate risk and cost. To the inexperienced, unit testing is overhead in the software development process that can be eliminated to deliver value to users faster. But what happens when the “overhead” is eliminated for years, the project has become more complicated, and more software developers have been added or experienced developer have departed. The result: a huge amount of risk to the project when changes are made, increased cost in time to test, and delayed delivery of value to the end users - a mountain of technical debt. Unit testing is a tool, if used properly, to help teams of all sizes be more efficient and minimize technical debt. It should not be viewed as “overhead” or a barrier to delivering value to the end users.
If you are developing software using the .NET (Microsoft) technology stack, there are several unit testing frameworks available. The most popular are Microsoft Unit Testing Framework for Managed Code (MSTest), NUnit, and xUnit.Net. All get the job done and warrant evaluation if you are considering adding unit testing to your project.
Unit testing is a tool and with all tools they can cause problems if not used properly. But when used properly, unit testing is a powerful tool in the tool chest. If you are starting a new project or supporting a legacy codebase, unit testing should be in your tool belt to help minimize risk and cost while delivering value to your end users.
Keep Right’in Code!