Code Generation


Remember the work you did earlier in this chapter to create your first unit test? Depending upon the degree to which you practice TDD, you might not create many of your unit tests in that manner. We created the first unit tests manually to help convey basic concepts, but Team System has support for automatically generating code. You may generate unit tests from your implementation code or generate limited implementation code when writing your tests.

Generating tests from code

If you have ever needed to add unit testing to an existing project that had none, it was likely a frustrating experience. Fortunately, Team System has introduced the capability to generate outlines of unit tests based on selected implementation code.

Note

If you are practicing test-driven development, described earlier, be aware that generating tests from existing code is considered a very non-TDD practice. In pure TDD, no implementation code should exist before unit tests are created.

Let's begin with the Functions class we used earlier in this chapter. Open Functions.cs and ensure it contains the following code:

     using System;     namespace ExtendedMath     {         public sealed class Functions         {             private Functions(){}             public static int Fibonacci(int factor)             {                 if (factor < 2)                     return (factor);                 int x = Fibonacci(--factor);                 int y = Fibonacci(--factor);                 return x + y;             }         }     } 

If you have been following the examples in this chapter, delete your existing FunctionsTest.cs file. Now you can right-click in your code and choose Create Unit Tests or choose the Create Tests button from the Test Views toolbar. The Create Unit Tests dialog will appear, as shown in Figure 14-7.

image from book
Figure 14-7

Note

Right-clicking in code is context-sensitive and the dialog will default appropriately based on where you clicked. For example, if you click from your class definition, all of that class's members will be selected for generation by default. Clicking on a method will default with only that method selected.

Select the members for which you would like to generate unit tests and then click the Settings button. The Test Generation Settings dialog will appear, as shown in Figure 14-8.

image from book
Figure 14-8

This dialog enables you to choose the behavior and naming conventions that will be used to generate your unit test code. For this first run, leave the settings at their defaults and click OK.

Team System will create a new class file, FunctionsTest.cs, if it does not already exist. Inside, you will find a class, FunctionsTest, marked with [TestClass] attribute.

At the top of the class is a TestContext field initialized to null, with a TestContext property to enable you to access context from your tests:

              private TestContext testContextInstance;              public TestContext TestContext              {                  get                  {                      return testContextInstance;                  }                  set                  {                      testContextInstance = value;                  }              } 

Next, you will find commented-out placeholder methods for ClassInitialize, ClassCleanup, TestInitialize, and TestCleanup, wrapped in a region:

            #region Additional test attributes            //            //You can use the following additional attributes as you write your tests:            //            //Use ClassInitialize to run code before running the first test in the     class            //            //[ClassInitialize()]            //public static void MyClassInitialize(TestContext testContext)            //{            //}            //            //Use ClassCleanup to run code after all tests in a class have run            //            //[ClassCleanup()]            //public static void MyClassCleanup()            //{            //}            //            //Use TestInitialize to run code before running each test            //            //[TestInitialize()]            //public void MyTestInitialize()            //{            //}            //            //Use TestCleanup to run code after each test has run            //            //[TestCleanup()]            //public void MyTestCleanup()            //{            //}            //            #endregion 

Finally, you will see the actual generated unit test:

            /// <summary=            /// A test for Fibonacci (int)            /// </summary=            [TestMethod()]            public void FibonacciTest()            {                int factor = 0; // TODO: Initialize to an appropriate value                int expected = 0;                int actual;                actual = ExtendedMath.Functions.Fibonacci(factor);                Assert.AreEqual(expected, actual, "ExtendedMath.Functions.Fibonacci did    not return the expected value.");                Assert.Inconclusive("Verify the correctness of this test method.");            } 

The generated code defines a basic structure that depends on the signature of the member being tested. In this example, it recognized that Fibonacci accepts an integer and returns an integer, including an Assert.AreEqual for you. The TODO indicates that factor and expected are only default values and need to be adjusted by you to ensure correct test values.

Note

Keep in mind that generated code will often benefit from careful refactoring, and your generated unit tests will be no exception. For example, look for ways to consolidate common tasks into shared functions. Refactoring is covered in Chapter 11.

Optionally, but by default, generated tests end with calls to Assert.Inconclusive to indicate that the unit test needs further inspection to determine its correctness. See the "Using the Assert methods" section for more information.

Generating code from tests

As you have just seen, generating tests from existing code can be very useful. It can be invaluable when you've taken ownership of a system that has no unit tests. However, adherents of test-driven development discourage such approaches, preferring to write test code before implementation code.

Fortunately, Visual Studio also has some support for this approach. You can write unit tests, including calls to classes and methods that do not exist, and the IDE will help you define outlines of their implementations automatically.

Begin by creating a new Class Library project called ResourceExample. While TDD purists prefer to avoid writing any implementation code before tests, Team System requires an existing class in which to place generated code. All you need is a basic class definition, so add a new class file called ResourceLoader.cs containing the following code:

     using System;     namespace ResourceExample     {         public class ResourceLoader         {         }     } 

Now you can write your unit test. It will verify that a call to the ResourceLoader class's GetText method with an identifier returns the expected resource string. Create a new test project named ResourceExampleTesting and set a reference to the ResourceExample project. Next, add a new class file called ResourceLoaderTest.cs with the following code:

     using Microsoft.VisualStudio.TestTools.UnitTesting;     using ResourceExample;     namespace ResourceExampleTesting     {         [TestClass]         public class ResourceLoaderTest         {            [TestMethod]            public void GetTextTest()            {                const int RESOURCE = 100;                const string EXPECTED = "Result for 100";                string actual = ResourceLoader.GetText(RESOURCE)                Assert.AreEqual(EXPECTED, actual);            }         }     } 

In the code editor, click on GetText. It should be underscored with a small rectangle indicating that one or more options are available. Hover the mouse pointer over the rectangle, click on the resulting button, and you will see "Generate method stub for ‘GetText’ in ‘ResourceExample.ResourceLoader.’" Alternately, you can right-click on GetText and choose Generate Method Stub. If you have trouble finding these options, make sure that you have set a reference from the test project to the ResourceExample project.

Choose either of these options and then switch to ResourceLoader.cs, where you will now see the following:

     using System;     namespace ResourceExample     {         public class ResourceLoader         {             public static string GetText(int RESOURCE)             {                 throw new Exception("The method or operation is not implemented.");             }         }     } 

You will notice that Visual Studio inferred the best signature for the method from the context of its use in the unit test. Also notice that the only action in the method is throwing an Exception. This is to remind you that there is work left to do. Replace the throw with whatever implementation code you need to make your test(s) pass, and then refactor and continue with your next test.



Professional Visual Studio 2005 Team System
Professional Visual Studio 2005 Team System (Programmer to Programmer)
ISBN: 0764584367
EAN: 2147483647
Year: N/A
Pages: 220

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net