utomated unit testing lets a team exercise its entire code base against a battery of tests, facilitating a quick, reactive environment by providing instant feedback during development. When developers can test changes to the code for validity, errors become apparent immediately. Code becomes simplerand the tests provide great example documentation too.
Today, it's rare to find a software development team that does not practice some form of testing. This typically takes the form of a QA department performing system tests at the end of a development cycle, right before a release. Any bugs found during this testing must be fixed and retested before the final release; potentially causing delays and release date slips.
With the increased interest in agile software development, unit testing has taken a more prominent role in the development cycle. Using automated, repeatable unit tests is fast becoming commonplace for many development teams to verify and test their code. Development teams do this as an active part of development, rather than at the end of development. Using an automated testing framework, teams can create a set of repeatable tests for anyone on the team to run against the current build of the system. Through these tests, development teams can verify that new code additions or a change does not break existing code.
In this article, I will introduce automated unit testing in a .NET environment. Many of the concepts described within are not new to Java and Smalltalk developers, however, many Visual C++ and Visual Basic developers, as well as .NET developers, may not be familiar with them.
Before I dive into an example component and its tests, let me address some of the core concepts and definitions for unit testing.
Unit Testing Overview
Unit tests exercise a small piece of functionality within a system. Typically, a test runs individual functions against known inputs, with the results verified against expected results. For example:
Debug.Assert( 2 == someFunction( 1 ) );
The above statement verifies that the output of someFunction
() is 2
when passed a value of 1
. This is an example of an inline unit test. You usually put inline unit tests inside your code and execute them at run time. The other type of unit test is the external unit test. You run external unit tests during development, usually as part of some build process. They are external in that there is no trace of external unit tests within the actual shipping code. Either a human or a machine can run these external tests.
Unit tests don't just test for successful outputs; they can also test for appropriate error and boundary conditions. The core concept of the unit test is that they appropriately exercise how the code will be used, and what the results should be.
Why Unit Test?
Since unit tests test individual units of a system, you can ensure that the unit implements the correct functionality and encapsulates the appropriate error handling. Units, in this case, can be methods, classes, packages, or even complete inheritance hierarchies.
|Since unit tests are used to test individual units of a system, you can ensure that the unit implements the correct functionality and encapsulates the appropriate error handling.|
You can use unit tests to document the correct and incorrect usage of a unit. A suite of tests organized into fine-grained individual tests provides an excellent example of how to use (and not use) the unit. Instead of a somewhat incomplete set of examples that a QA group must maintain, a suite of exhaustive unit tests provides a complete view of a system and its functionality.
Some teams use unit tests as a safety net for the team. When taken as a whole, they comprise a very powerful battery of tests to use whenever you wish. This means that activities such as fixing bugs and refactoring will go more smoothly because any changes that break existing code will automatically cause the tests to fail. Developers receive feedback as soon as the next time they run the tests.
Approaches to Unit Testing
You can test code manually or automatically. There are several benefits and drawbacks to each form of testing, which I will discuss below:
Manual unit testing
usually involves a debugging session within an IDE, utilizing breakpoints and step-through debugging.
Highly visible to current developer.
Since the code is fresh in the developers' minds, any bugs can usually be fixed rather quickly.
The developer must manually setup any initial state to perform the testing. This can be time consuming and difficult, especially if a complex state is required.
This type of testing can be error prone and time consuming because the developer cannot test all possible inputs / outputs.
There is no way to track the results of this type of test.
There is no way to track the code coverage of this type of testing. This could lead to untested code.
There is no way to integrate your testing with the testing that is done by the rest of your team. This could lead to changes in your code negatively affecting another team member's code.
Because developers must run this type of test manually, there is no way to build up a suite of tests and make them repeatable.
Any external resources used or modified during the test must be manually cleaned up. This could include database records, mainframe records, or temp files on the file system.
Automated unit testing
involves programmatically writing tests for your code. Typically you collect these tests into a specific collection of tests called a suite
. You can then run the suite of tests against your code using several different means, manual or automated.
- Allows you to build a battery of tests to run your code against at any time.
- Keeps code syntax fresh in your mind by providing comprehensive documentation for your code in the form of examples.
- Provides an easy way to make sure new code changes did not break existing code.
- Changes to the code are easier to make because of automatic feedback and coverage that automated unit testing provides. Any code breaking changes will be known because the tests will fail.
- The entire battery of tests can be run against the code without user intervention. This enables the tests to be run during an external event, such as during the build process.
- There is a higher startup cost associated with automated unit testing.
- Even though the benefits of automated unit testing can be seen when implemented by a single developer, to reach full usefulness, the entire code base should have automated tests, not just part of it. This requires that the entire team adopt the practices of automated unit testing. This way, there can be a guarantee of complete code coverage.
- Tools must be chosen to aid in the creation and execution of the automated unit tests.
Despite the drawbacks, automated unit testing provides a clear advantage over manual unit testing. The rest of this article will focus on utilizing automated unit tests within the .NET environment.