Software QA FYI - SQAFYI

A Unit Testing Walkthrough with Visual Studio Team Test

By: Mark Michaelis

Summary: Learn about the unit testing features of Team Test from a TDD, test-then-code approach with this walkthrough. (24 printed pages)

Introduction

With the latest release of Visual Studio Test System (VSTS) comes a full suite of functionality for Visual Studio Team Test (TT). Team Test is a Visual Studio integrated unit-testing framework that enables:

  • Code generation of test method stubs.
  • Running tests within the IDE.
  • Incorporation of test data loaded from a database.
  • Code coverage analysis once the tests have run.

In addition, Team Test includes a suite of testing capabilities not only for the developer, but the test engineer as well.

In this article, we are going to walk through how to create Team Test unit tests. We begin by writing a sample assembly, and then generating the unit test method stubs within that assembly. This will provide readers new to Team Test and unit testing with the basic syntax and code. It also provides a good introduction on how to quickly set up the test project structure. Next, we switch to using a test driven development (TDD) approach in which we write unit tests before writing the production code.

One of the key features of Team Test is the ability to load test data from a database and then use that data within the test methods. After demonstrating basic unit testing, we describe how to create test data and incorporate it into a test.

The sample project we are going to use throughout this article contains a single LogonInfo class. This will encapsulate the data associated with logging-on, data such as the user name and password, along with some simple validation rules for that data. The final class appears in Figure 1.

Note that all test code will appear in a separate project. Where reasonable, production code should be affected as little as possible by test code, so we don't want to embed the test code within the production assembly.

Getting Started

To begin, we create a Class Library project named "VSTSDemo." By default, the Create directory for solution check box is selected. Leaving this option will enable us to create the test project in a separate directory alongside the VSTSDemo project. In contrast, deselecting this option creates a directory structure in which Visual Studio 2005 will place the test project into a subdirectory of the VSTSDemo project. The test project follows the Visual Studio convention of creating additional projects in subdirectories of the solution files path.

After creating the initial VSTSDemo project, we use the Visual Studio Solution Explorer to rename our Class1.cs file to LogonInfo.cs. This will also update the class name to be LogonInfo. Next, we modify the constructor to accept two string parameters named userId and password. Once the constructor signature is declared, we are ready to generate the tests for the constructor.

Creating a Test

Before we begin writing any implementation for LogonInfo, we follow the TDD practice of first writing a test. TDD isn't required for using Team Test, however, it is best practice we follow through the rest of this article. We do this by right-clicking on the LogonInfo() constructor and selecting the Create Tests... menu item (shown in Figure 2). This will display a dialog for generating unit tests into a different project (see Figure 3). By default, the Output project setting is for a new Visual Basic project, but C# and C++ test projects are also available. For this article, we will select Visual C# and click the OK button, followed by entering a project name of VSTSDemo.Test for the project name.

The generated test project contains four files related to testing.

File Name Purpose
AuthoringTest.txt Provides notes about authoring tests including instructions on adding additional tests to the project.
LogonInfoTest.cs Includes generated test for testing the LogonInfo() constructor along with methods for test initialization and cleanup.
ManualTest1.mht Provides a template to fill in with instructions for manual testing.
UnitTest1.cs An empty unit test class skeleton in which to place additional unit tests.

Because we aren't going to be doing any manual testing in this project, and because we already have a unit test file to work with, we will delete ManualTest1.mht and UnitTest1.cs.

In addition to some default files, the generated test project contains references to both the Microsoft.VisualStudio.QualityTools.UnitTestFramework and the VSTSDemo project, which the unit tests are executing against. The former is the testing framework assembly the test engine depends on when executing the unit tests. The latter is a project reference to the target assembly we are testing.

By default, the generated test method is a placeholder with the following implementation:

Listing 1. Generated test method, ConstructorTest(), inside VSTSDemo.Test.LogonInfoTest

   
/// <summary>
   ///This is a test class for VSTTDemo.LogonInfo and is intended
   ///to contain all VSTTDemo.LogonInfo Unit Tests
   ///</summary>
   [TestClass()]
   public class LogonInfoTest
   {
      // ...

      /// <summary>
      ///A test case for LogonInfo (string, string)
      ///</summary>
      [TestMethod()]
      public void ConstructorTest()
      {
         string userId = null; // TODO: Initialize to an appropriate value

         string password = null; // TODO: Initialize to an appropriate value

         LogonInfo target = new LogonInfo(userId, password);

         // TODO: Implement code to verify target
         Assert.Inconclusive(
            "TODO: Implement code to verify target");
      }
}

The exact generated code will vary depending on the method type and signature that the test targets for testing. For example, the wizard will generate reflection code for testing private member functions. In this particular case, we have code specific for public constructor testing.

There are two important attributes related to testing with Team Test. First, the designation of a method as a test is through the TestMethodAttribute attribute. In addition, the class containing the test method has a TestClassAttribute attribute. Both of these attributes are found in the Microsoft.VisualStudio.QualityTools.UnitTesting.Framework namespace. Team Test uses reflection to search a test assembly and find all the TestClass decorated classes, and then find the corresponding TestMethodAttribute decorated methods to determine what to execute. One other important criteria, validated by the execution engine but not the compiler, is that the test method signature be an instance method that takes no parameters. The name is irrelevant because reflection searches for the TestMethodAttribute.

The test method (ConstructorTest()) instantiates the target LogonInfo class before asserting that the test is inconclusive (Assert.Inconclusive()). When the test is run, the Assert.Inconclusive() provides an indication that it is likely to be missing the correct implementation. In our case, we update the ConstructorTest() method so that it checks the initialization of user ID and password as shown below.

Listing 2. Updated ConstructorTest() implementation

      
/// <summary>
      ///A test case for LogonInfo (string, string)
      ///</summary>
      [TestMethod()]
      public void ConstructorTest()
      {
         string userId = "IMontoya";

         string password = "P@ssw0rd";

         LogonInfo logonInfo = new LogonInfo(userId, password);

         Assert.AreEqual<string>(userId, logonInfo.UserId,
            "The UserId was not correctly initialized.");
         Assert.AreEqual<string>(password, logonInfo.Password,
            "The Password was not correctly initialized.");
      }

Note that the checks are done using the Assert.AreEqual<T>() method. The Assert method also supports an AreEqual(), without generics, but the generic version is almost always preferred because it will verify at compile time that the types match—a common error in unit testing frameworks available before generics were supported in the CLR.

Because instance fields or properties for UserId and Password have not yet been created, we need to go back and add these to the LogonInfo class as well, in order for the VSTTDemo.Test project to compile.

Even though we don't yet have a valid implementation, let's proceed to running the tests. If we follow the TDD approach, we shouldn't be writing any production code until we have tests demonstrating that we need such code. We violated this principle in order to get the project structure established, but once it has been set up, it is easy to follow the TDD approach throughout.

Full article...


Other Resource

... to read more articles, visit http://sqa.fyicenter.com/art/

A Unit Testing Walkthrough with Visual Studio Team Test