Welcome!

.NET Authors: Bruce Armstrong, Pat Romanski, Liz McMillan, Yeshim Deniz, Maureen O'Gara

Related Topics: .NET

.NET: Article

Unit Testing with Whidbey

A concise look at Microsoft's impressive offering

Do you want to improve the quality of your software? Does your budget require you to shorten your development life cycle? Still searching for an effective way to provide requirements to development teams, internal and outsourced? If you answered yes to any of these questions, look no further than test-driven development (TDD).

This unique approach to software development, created in 1989 as a component of eXtreme Programming, includes the creation and execution of automated unit tests before writing a line of implementation code. Coupled with a thorough design of your software, TDD will help reduce ambiguity in communication, ensure efficient software construction, and help reduce the amount of defects with your software.

Whether your environment follows TDD's test-first approach (test, code, refactor), code-first approach (interface/test-code, refactor), or simply uses automated unit tests to support their traditional development, build, and deployment cycles, the new features included in Visual Studio 2005, Team Developer and Team Test editions, will satisfy your quality needs. This article offers a concise look at the timesaving features included in Microsoft Visual Studio 2005 Team System's Unit Testing Framework, using a code-first approach to TDD.

What is TDD's Code-First Approach?
The code-first approach to TDD requires developers to follow these tasks:

  1. Thoroughly design your software. Know exactly what the software must accomplish before writing a single test.
  2. Use this design to create the interfaces and public methods for your objects (no implementation code).
  3. Use design requirements to build and execute tests, and watch them fail (Red).
  4. Write our implementation code and rerun our tests to watch them pass (Green).
  5. Interrogate the results and performance, tweak the code as necessary for structure, performance, etc. (Refactor).
Following this approach results in generation of a test suite, or logical grouping of automated unit tests that:
  • Are based on functional and technical design requirements
  • Are built up front, before implementation coding begins
  • By following defined naming conventions and standards, represent a structured and reference-able example of how to consume, deploy, and interact with your components
Feature #1 - Test Case Code Generation
When considering TDD for the first time, some of the most common concerns are that it "requires too much code, adds additional hours to the estimate," or worse yet, "requires additional resources dedicated to these tests." While this approach does require additional code, the amount of code needed tends to be exaggerated. First, component developers already create their own unit testing logic, but usually in a different format - possibly a console or a WinForm application. These applications tend to be undocumented, and allow for ambiguity for the consumer of the components. Second, having designed the components up front, the time spent on brainstorming of tests should be dramatically less than the time actually coding these tests. Many TDD followers believe that the time spent building unit tests is dramatically less than the time spent on resolving defects raised during the quality assurance process.

One of the most beneficial features of Unit Testing in Visual Studio 2005 Team System is the ability to dynamically generate code for base unit tests. This code generation engine provides an easy way to customize and enforce desired standards and naming conventions for yourself or a large group of developers.

To begin this process, we'll create a new class library project that will represent the test suite, or grouping of text fixtures. Simply select File>Add>New Project and create your test suite project. Now that we created the destination project it's time to generate code for our tests. There are two ways to launch the Create Tests wizard. You can either use the icon located in the Test Views toolbar, or right-click any code element in the code editor (Namespace, Class, or Method) and select Create Tests.... Once clicked, Visual Studio 2005 displays the Unit Test Generation dialog, which dynamically builds a checkbox tree view that allows you to:

  • Select the which classes and methods to generate tests (see Figure 1 - tree view with automatic selections)
  • Select the project that will contain your test suites (Figure 1 - Output Project dropdown)
  • Enforce naming conventions during the code generation process on File, Class, and Method names
  • Control the format of the generated code by enabling/disabling other options, such as marking tests inconclusive, enabling comment warnings and document comments, and generating tests for class members and items that already have tests
For our example, we'll accept all the default values except for one, Output Project. By default, the dropdown control is set to the current project. Use this dropdown to select the test fixture project and click OK. Visual Studio 2005 will:
  • Add a project reference to the Microsoft Unit Test Framework assembly.
  • Generate the [TestClass], or test fixture, which contains one to many tests for a given class.
  • Generate [Test] methods, one for each method selected in the wizard.
  • Generate [TestInitialize] and [TestCleanUp] methods that the test runner will execute before and after the execution of each [Test] method.
  • Generate basic logic for each [Test] method, including
    - Basic method comments
    - Object creation (for object to test, as well as any parameters passed to the method)
    - Stubbed method call
    - Assert.Inconclusive(msg), which is a placeholder for your tests. You will spend a lot of time with the Assert method, as it is the primary vehicle to determine if the test was successful or not.
Listing 1 is a sample [Test] method that was automatically generated by the Visual Studio 2005 Team System Unit Testing Framework.

Because the Unit Testing Framework generated sample methods specific to our implementation, all that is required is to complete the test code and replace the Assert.Inconclusive with Assert method calls needed to pass the test.

Feature #2: Unit Testing/Visual Studio 2005 Integration
At this point, we've generated the interfaces for our classes and test methods for our tests. The next step in the TDD process is to execute the tests, and watch them fail. TDD defines a test runner as an application that discovers, executes, and reports the results of automated unit tests. Traditionally, test runner applications usually come in GUI or console forms, and execute outside of the development environment. However, Visual Studio 2005 continues to be the single window into the development world with the integration of these results directly within its own user interface.

The Visual Studio 2005 IDE includes four main windowpanes used for unit testing, which are viewable using the View, Quality Tools menu option. First is the Test View, which lists all the methods containing [Test] attributes for each project in the solution. Next is the Test Explorer, which provides the ability to search, filter, and execute any number of included tests. Once the test execution is complete, Visual Studio 2005 will display the status for each test in the "Test Results" window pane. Finally, if you requested code coverage instrumentation, those metrics are located in the Code Coverage Results dialog.

From the View menu, selecting the Quality Tools/Test Explorer menu item will display the Test Explorer dialog. Visual Studio 2005 uses reflection to dynamically discover each test suite, fixture, and test method uses this information to populate this dialog box. This window provides extensive searching and filtering options to help manage the execution of a large number of tests.

Basically, you'll need to select which tests you want to execute. Clicking on entries in the left-hand navigation will select the respective tests on the right portion of the window. For our example, we'll want to execute all the tests. Follow these steps to accomplish this task:

1.  Click the [All Tests] option on the left-hand navigation. Once clicked, Visual Studio 2005 will automatically select every test listed in the right-hand list view.
2.  Click the green play button to execute the tests.

The first time you attempt to execute your tests, Visual Studio 2005 will ask you to create a Unit Test Configuration, which results in a new *.rcg file added to the solution. Visual Studio creates the file from settings entered in the Select a Test Run Configuration dialog. This dialog allows you to perform the following functions:

  • Set notifications options for this dialog.
  • Machine Configuration: Where would you like your tests executed, locally or remotely?
  • Code Coverage: Calculate and report statistics on how much of the code was actually executed.
  • Deployment: If you wish to execute these tests remotely and they require additional assemblies for execution, they must be included here.
For now, we'll select the defaults with an additional setting for Code Coverage. To accomplish this:
  • General: Browse and load the LocalTestRun.rcg file template that exists in the local templates directory (Visual Studio 2005 will locate this file for you automatically).
  • Code coverage: Select the checkbox labeled Enable Code Coverage Instrumentation and include any assemblies in the To instrument list box.
  • Click OK to begin the execution of the unit tests.
Once execution begins, Visual Studio 2005 displays the Test Results windowpane, as shown in Figure 2. The main purpose of this window is to communicate test execution results for current and previous tests. The IDE will populate this window with each test selected in the Test Explorer dialog, execute the tests, and display the color-coded result for each test. Right-clicking any of the test results provides the ability to either (a) view the results in the Output dialog, or (b) go straight to the source code that caused the error.

At this point in the TDD process, we have (a) created our interfaces, (b) created our tests (via code generation), (c) coded our tests based on our requirements, (d) executed our tests, and watched them fail (red). Now is the fun part - generating the implementation code to satisfy our design requirements. Once coding is complete, it is time to rerun our tests to watch them pass (green). To accomplish this, simply edit the code for your class library, re-execute the tests, and monitor the results in the Test Results windowpane.

Feature #3: Code Coverage - How Much Code Actually Executed?
The final step of the code-first TDD process is to refactor our code. Martin Fowler defines "refactoring" as, "... a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior." My personal belief is that the need to refactor comes from many drivers, including adherence to object-oriented design (OOD) and to improve performance. Adhering to OOD is easier to accomplish because basic OOD standards/ guidelines currently exist, and hopefully were considered during the project's design phase. Performance, on the other hand, is a bit more challenging, especially if the developer is not armed with appropriate metrics to help identify the problem areas.

Microsoft recognized this need, and built the "code coverage" feature into the Unit Testing framework. This feature provides developers with valuable, quantitative metrics obtained directly from executed code. Specifically, code coverage provides the number and percentage of lines of code (a) executed, (b) not executed, and (c) partially executed, for each referenced assembly in your project down to an individual method level.

To enable code coverage statistics, navigate to the Code Coverage tab in the Test Run Configuration dialog that appears when you execute your tests. In this dialog, you must complete two steps. First, select the checkbox, "Enable code coverage instrumentation. Second, select which assemblies to calculate statistics on by selecting the assemblies on the left and "add"ing them to the list box on the right, labeled "To instrument."

Upon completion of your test, click the Code Coverage dialog to view the results, represented in Figure 3. For each test execution, Visual Studio 2005 will dynamically build a tree view, which provides the developer with drill-down capability for these metrics.

With a simple click of a button, Visual Studio 2005 will highlight each line of source code with color-coding to help identify which lines were completely, partially, or not executed. Notice that Visual Studio 2005 loaded the "Code Coverage" toolbar in the IDE during test execution. To determine which lines of code executed, simply click the first button on this toolbar labeled "Show Code Coverage." With this button selected, the IDE will present the source code with the appropriate color coding.

Conclusion
The Unit Testing framework provided by Microsoft Visual Studio 2005 Team System helps save time and enforces standards through code generation. It gracefully continues the trend of being a single development portal with the tight integration with Visual Studio IDE. Finally, it arms developers with the necessary information in order to make good "refactoring" decisions. The use of TDD and Microsoft Visual Studio 2005 should help find defects before the quality assurance process, and help deploy more efficient software ahead of schedule.

References

  • Visual Studio 2005 Team System Page: http://lab.msdn.microsoft.com/vs2005/teamsystem/default.aspx
  • Visual Studio 2005 Team System Walkthrough Projects: www.gotdotnet.com/workspaces/ workspace.aspx?id=1fa266bc-24ce-4695-be6d-e86b28ef1176
  • Visual Studio 2005 Team System Pre-release Documentation: http://workspaces.gotdotnet.com/vstsdocs
  • Fowler, Martin. Refactoring. www.refactoring.com
  • More Stories By Scott Dockendorf

    As a senior software architect for »telligentsystems, Scott Dockendorf specializes in architecting and developing enterprise applications in .NET. Scott is passionate about secure component development, enterprise integration, SOA, and helping companies adopt best practices through proven methodologies.

    Comments (2) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    Scott Dockendorf 12/07/04 05:21:50 PM EST

    Ron - thanks for the feedback. Yes, it was a dyslexic comment, and should have been 1998.

    Sorry for the mistake.

    Ron Jeffries 12/07/04 04:25:35 PM EST

    Nice article, and sounds like some good features in WHidbey. As a .NET user / programmer / author, I've been wanting Microsoft to supply more of the features that we see in the (excuse me) Java world. It's nice to see progress on that. I better start trying out my Beta!

    One minor thing: TDD and Extreme Programming weren't around in 1989. The first XP project started in 1996. Of course a lot of the ideas of XP have been around a long time, but the first I ever saw TDD as it is described today was after 1996, maybe around 1998. Maybe 1989 is a typo for 1998?

    Ship it, that's my advice! Thanks!
    Anyway,