Showing posts with label Test Driver Design. Show all posts
Showing posts with label Test Driver Design. Show all posts

Sunday, November 9, 2008

Test Driven Development with Visual Studio 2008 Task Lists

Totally Integrated TDD Example

In my previous blog post, Integrated TDD Environment, I explained how you can do all of your Test Driven Delopment steps without leaving the Visual Studio 2008 IDE by adding a new Task List comment type: TEST

In this blog I will demonstrate step-by-step using a simple example how create a functional class from scratch using integrated TDD within VS 2008.

The Requirements

Every software development project should begin with requirements. Our example will have the following requirement:

  • Provide a web form where the user can enter a temperature in either Fahrenheit or Celsius and see the conversion to the other system.

Our design will separate the user interface from the business logic. For our example we will only test the business logic. We start by creating a new Windows class library project. Name it WebControls. Delete Class1.cs and add a new class named Temperature. This is the class we will build using TDD.

Generate the Test Project

Since we are doing TDD, then all of the development will be driven from our unit tests. We want to have a test project for each project in our solution and a test fixture for each class in each project. Lets add a new project to our solution by selecting Project Type, "Test" and the "Test Project" template. Name the project "WebControlsTestProject."

Select WebControlsProjects in the solution explorer and select "Add" then "Unit Test." You can then browse all the projects in your solution. Select the Web Controls project and the Temperture class:

Visual Studio will create your unit test module shell with the necessary includes and several procedures, including many that are commented out in the "Additional test attributes" region, including setup and tear-down routines, which you can easily uncomment and use.

Since we created a class named "Temperature" then the Visual Studio unit test wizard created a class called "TemperatureTest." We will add our test list in this module.

Add TEST to the Task List Comment Tokens

Start by defining a new Task List token for the test list. On the Tools menu select Options, then select Task List from the list on the left of the options dialog.

Type "TEST" in the Name field, click Add and then OK. Now you will be able add test list items anywhere in your code by adding a comment that begins with the TEST: followed by the description of your test:

// TEST: Here is a unit test description.

This comment now appears on the Comments Task List:

Create the Test List

We are ready to create our test list. We start with our requirement and test for the following:

  • Valid results
  • Error conditions
  • Limit conditions

If you remember back to 6th grade, these are the key facts concerning conversion between Fahrenheit and Celsius:

  • Water freezes at 32 F and 0 C
  • Water boils at 100 C and 212 F
  • The formula for converting is C = (5/9)*(F - 32)
  • There is a temperature called "absolute zero" which is the lowest posible temperature. It is -273.15 C or -459.67 F

We can make of these facts to generate the initial test list and then add more tests as we think of them. Open the TemperatureTest.cs class file, move to the end, and insert the following comments before the two end brackets for class and namespace:

// TEST: Verify 32 F converts to 0 C // TEST: Verify 0 C converts to 32 F // TEST: Verify 100 C converts to 212 F // TEST: Verify 212 F converts to 100 C // TEST: Verify that the Celsius temperature equals Farenheit minus 32 times 5/9 // TEST: Verify that the Farenheit temperature equals Celsius times 9/5 plus 32 // TEST: Verify that a negative Farenheit temperture converts to the correct value // TEST: Verify that a negative Celsius temperture converts to the correct value // TEST: Verify that entering a temperture less than -273.15 C throws and exception

} }

If this doesn't cover all the code we need to write, it should give us a good start. We can easily add new tests as we think of them just by entering comments. Our VS Task List should now look like this:

image

Implementing the Tests

We're now ready to start TDD. Let's start with the first test, "Verify 32 F converts to 0 C." You can double-click the item in the task list to jump right to it. Add the following code below the comment:

Code01

Now we want to write some code and see if the test passes. First we have a design decision to make: do we want this class to require an object to be instanited or use static methods? Static methods are easier to call, but if we need data persisted within the object we have a problem because static methods are global. If you look back at the requirement we see that we need to support a web form, which would have a view state, so our temperture convertor probably won't need to remember anything. We'll start with a static method and hopefully that'll get through all the tests.

We start by writing the code in the test first, then updating the Temperture class so that it compiles, and eventually passes the test.

f1

It seems reasonable that the Temperature class would have a static method named "ToCelsius" that takes a double (since we can have fractions of a degree) representing the Fahrenheit temperature and returns the Celsius temperture as a double.

We have not written the "ToCelsius" method yet, so Visual Studio puts a wavy red line under the method indicating a syntax error. Here is where the refactoring capabilies come in handy. Hold the mouse over the "ToCelsius" method name then you get a drop list where you can select "Generate method stub for Temperature.ToCelsius." We do so and now we can compile and run the test. Of course it fails:

f2

To fix the test we right-click the ToCelsius method "Go To Definition," and then fix the method with the minimum amount of code for the test to pass:

f3

Run the test again and now it passes:

f4

Our first test is done, so we remove it from the list. Since our Task List is driven by comments then we need to delete the TEST: from the comment. We can leave the description so that we have some documentation left behind.

f5

The Task List now has 8 tests remaining. The next test is to verify that 32 F converts to 0 C. If we follow the same method as before we write this test:

f6

We implement this method to get the tests to pass:

f7

And we run all the tests and verify they pass:

f8

Note that there are actually 3 tests, because Visual Studio automatically adds a test for the class constructor. Since we're using static methods and don't do anything in the construtor, then I changed the default test to simply verify that the class can be created:

ct

When we did the freezing point tests we wrote the minimum amount of code by simply returning the hard-coded expected results of 0 or 32. We could do the same with the boiling points, but let's skip ahead a couple tests and see if these two tests don't take care of some of the others:

// TEST: Verify that the Celsius temperature equals Farenheit minus 32 times 5/9 // TEST: Verify that the Farenheit temperature equals Celsius times 9/5 plus 32

Again we start by writing the test code first, then adjusting the class methods for the tests to pass. We can use the same to static method to convert Fahrenheit to Celsius and copy the previous test, then edit the code to create the test below:

to Celsius

For the above test I picked a random temperature, 72 F, and then calculated what the result should be. We now run the test, and since our "ToCelsius" mthod is still hard-coded to return 0 then, of course, it fails:

to celsius test result

We can now go to the ToCelsius method and fix it:

to celsius method

Let's rerun the tests and see what happens:

Notice that not only does the ConvertToCelsiusTest pass, but the previous tests also pass so that we know we have not broken any of the previously working code with our new changes. That's one of the benefits of TDD: as you build your software in small steps you build a collection of regression test that get re-run at each step, immediately alerting you when when new code causes previous code to fail.

At this point I won't bore you too much more with doing the next few tests step-by-step. The Convert to Fahrenheit test can be done the same way we just did the Celsius conversion, and the two boiling point tests can be added and run to check that our formulas work at both ends of the scale:

tests

Now we have one more test to do:

// TEST: Verify that entering a temperture less than -273.15 C throws and exception

But this suggests one companion test, verifying that absolute zero on the Fahrenheit scale also generates an exception. We can quickly add tests as we think of them by simply typing in a comment:

// TEST: Verify that entering a temperture less than -459.67 F throws and exception

Let's do absolute zero on the Celsius scale first. This is different from the previous tests, because instead of testing an assertion, we want to test that certain will generate an exception. So we need to use the ExpectedException attribute. We add it as a decoration inside square brackets between the TestMethod() attribute and the unit test method declaration. It takes a parameter for the expected exception type, so that's the first thing we need to decide, the type of exception we should generate. We could define our own custom exception, but lets just use an Overflow Exception, since we are overflowing the range of valid temperatures. Here's what the test code would look like:

Abs0CelsiusTestCode

Notice that we added the ExpectedException attribute and gave it the System.OverflowException parameter, using the typeof() function to provide the correct type for the parameter. The test code itself does not need to use the Assert method. The test method just needs to execute code we expect to generate the exception. So we take the out-of-range Celsius value, give it to the ToFahrenheit method, and try to assign it to a variable. When we run this test we get the following result:

The code did not throw an exception, therefore the test failed. We now adjust the ToFahrenheit to throw the expected exception:

Abs test fix

The test now passes:

Abs0CelsiusTestFixResult

The process for the absolute zero Fahrenheit is similar. We add the test, adjust the code ustil it passes, then we can re-run all our tests and they now all pass:

We keep removing the TEST: attribute from our comments each time a test passes and our Task List is now empty:

Summary

This demonstrates how you can do TDD, Test Driven Development, without leaving the Visual Studio 2008 environment. We added a new task type, TEST and then managed our TDD test list through the Task List. We used the integrated unit test and refactoring capabilities of VS 2008 to build a new class from scratch using the red/green/refactor methodology.

Saturday, October 11, 2008

Use Visual Studio Task List to Create Test Lists for Test Driven Development

Integrated TDD Environment

Introduction

The Visual Studio 2008 SP1 now includes integrated unit testing, making TDD (Test Driven Development or Test Driven Development) easier than ever before. However, if you want to use VS 2008 as a fully-integrated TDD environment, you'll need to make creative use of the Task List.

Test Driven Design and Development

The idea behind TDD is that you write all the code for your application through a series of small steps by creating unit tests. The mantra is, "Never write a single line of code unless you have a test that fails." There are a couple advanatages to doing it this way:
  • By doing things in small steps, you can go quicker with less errors
  • You create unit tests as you go and rerun them with each coding change, immediately finding any code that gets broken when you add new features

So how do you start?

Like any software project, TDD starts with requirements. What does the software need to do? You take each requirement and generate a "test list," a list of all the criteria that must be met to fulfill the requirement.

The basic TDD process is Red/Green/Refactor:

  1. Write a test that fails so that the unit test shows a red light
  2. Add code to make the test pass and show a green light
  3. Refactor to avoid duplication

Using Visual Studio as TDD Environment

Visual Studio now helps with this process. Visual Studio 2008 Professional Edition now includes the Visual Studio Test Suite (previously only Team Edition had it) which gives you the red and green lights. The Visual Studio editor also includes some built-in refactoring support.

However I would like to do everything within a single IDE if possible. I would prefer to do the Test Lists within my code editor rather than entering them into a spreadsheet, text file, or writting them down on paper. One way to do this is with the Visual Studio Task List.

Visual Studio Task List

The Task List has been part of VS since the original .Net 1.0 release. It provides a mechanism for reminders of things that developers need to complete.

You can open the Task List by selecting it from the View menu. There are two types of tasks: User and Comments. User tasks are things that you enter manually and manage yourself. Comments are created by adding comments with special key words to your code. You can then jump from your task list to the spot in the code where you put the comment.

Visual Studio provides three default tokens for comment tasks:

  • TODO: Something that needs to be started or completed. VS will automatically generate some of these, for example when you create a new class it adds, "TODO: Add constructor logic here."
  • HACK: Quickly written code that needs to be reworked later.
  • UNDONE: Code changes that have been reversed.

To create a comment you enter the comment characters, (// in C#, ' in VB), the token followed by a colon (:) and the text of the task.

We can use the Task List for TDD by adding our own comment token: TEST.

To add a custom token we select options from the Tools menu and then select Task List under Environment:

To add our new token we enter TEST in the Name field and then click Add. We can now add a Test Task anywhere in our code by starting a comment, typing TEST: and a description of our test. Now you can create your TDD test list right inside the VS source code editor for your test project.: // TEST: Verify a user can add a new record // TEST: Verify a user can update an existing record // TEST: Verify a user can delete an existing record // TEST: Verify an error occurs when add a duplicate record To implement each test you add your test code after the comment and delete the TEST: part of the comment to remove the test from the Task List:

// Verify a user can add a new record [TestMethod()] public void AddNewRecord() { // TODO: Add Test Logic }

As you think of new tests while your in the middle of coding others you add a TEST: comment and then go back to what you're working on. Later, you can double-click on an item in the Task List to go directly to that spot in the code.

In my next blog post I'll walk you through a simple example.