Unit Testing: What is it, exactly?
Even though Unit Testing is becoming much more popular with developers (and with good reason in my honest opinion), I still hear a lot of questions at conferences and user groups from people about just what Unit Testing means. In this article, we start at the top and cover the fundamental building blocks of Unit Testing, and then I will build on those in my next few articles.
At the core, Unit Testing is the practice of writing code that can be run interactively or automatically to exercise (or test) isolated blocks (or units of work) of an application (the System Under Test). That definition is a bit circular, so I will cover the pieces and parts you need to know to write Unit Tests.
The System Under Test
This is the code being tested. Typically, this is lines of business code, also referred to as “production” code. In a non-unit testing world, this is the code that you and your team are writing to solve a particular business problem (or set of problems).
Unit of Work
A Unit of Work is the smallest piece of an application that can be tested in isolation. For object oriented languages (such as Java or the .NET languages C# and VB.NET) this is a method. For database development languages (such as SQL Server’s T-SQL), a Unit of Work is a stored procedure or a user defined function.
This is what separates Unit Testing from Integration or System Testing. When writing unit tests, you are testing a single building block of an application as opposed to Surface Testing or System Testing.
Unit Test Framework
A Unit Test Framework provides the mechanisms to distinguish methods (or stored procedures) as tests. Additionally, most frameworks also provide a Test Runner so the tests can be executed either from your development environment or scripted as part of the build process.
Unit Test Frameworks are available in many languages and styles. Typically, developers choose a framework written in the same language as the System Under Test, but there isn’t a requirement to do so, and there can be compelling reasons to use a framework written in a different language.
The Unit Test is the code that does the work of exercising a single Unit of Work. The goal of a Unit Test is to verify that the System Under Test works as the developer expects. Unit Tests (in managed code) are written as single methods that are decorated with metadata (such as attributes in .NET) that identify them as tests to the Test Framework.
Unit Test Structure
The Unit Testing community has largely settled on the AAA method for writing Unit Tests. The “A”’s represent the three logical sections of a unit test and stand for Arrange, Act and Assert. This structure helps make the spirit and goal of Unit Tests more apparent to those who read them later.
This is where any prerequisites for the action are set up. This can be the creation of test stubs (or mocks), adding data into the database, or anything else that the System Under Test needs to execute properly. This section is typically fairly small, but it can vary in size. A significant size (more than just a few steps) is usually a code smell and indicates that the unit of work isn’t granular enough or the coupling is too high.
This is where the Unit of Work is executed. It’s important that there is only one action in any unit test. Multiple actions can occlude the results and make it unclear exactly where the failure occurred. Remember, this is a Unit Test – not an Integration Test.
This is where the rubber meets the road. Everything has been arranged, the action has been executed, now the results need to be validated. In managed code this is accomplished by calling methods that clearly state what the expected result is and comparing them to the actual result, such as AreEqual(expected,actual), IsTrue(actual).
Code Coverage represents the percentage of code from the System Under Test that gets executed when all of the Unit Tests are run.
Stubs and Mocks are stand-ins for dependencies required by the Unit Of Work. They are used to further test isolation by removing code that could error out and provide misleading results to the Unit Test.
This article sets a foundation that we will build on in my next few articles. In the next article, I will discuss why you should care about unit testing.