Silverlight Test Driven Development – Part II - Jesse Liberty - Silverlight Geek Page view counter

Silverlight Test Driven Development – Part II

MiniTutorialLogo

 

 

 

 

At the end of Test Driven Body Snatchers I promised to follow up with a walkthrough of real-world Test Driven Development. What gets into me? 

Well, a promise is a promise. 

Walking Through Test Driven Development

Waybackmachine3[1] The set up is this: We’ve designed the  Silverlight HyperVideo Platform using the MVVM pattern

We’re going to take a trip in the way-back machine, and design  the ItemsViewModel class (the VM in MVVM)  using Test-Driven Development. 

 

Begin At The Beginning…

We start out by examining the existing ItemsView class (the V in MVVM ) which is implemented in ItemsView.xaml (and its code-behind):

<UserControl x:Class="SilverlightHVP.View.ItemsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
           d:DesignHeight="300"
             d:DesignWidth="400">
   <Grid x:Name="LayoutRoot" Background="White">
      <ListBox Name="ItemsList"
               Background="Beige"
               Width="200"
               Height="250"
               HorizontalAlignment="Center"
               VerticalAlignment="Top"
               ItemsSource="{Binding Items}"
               SelectedItem="{Binding CurrentItem, Mode=TwoWay}"
               DisplayMemberPath="TextToDisplay">
      </ListBox>

   </Grid>
</UserControl>

Designing the View Model

We are now ready to create the ItemsViewModel, which will serve as the data-context for the binding shown in the ListBox.

iStock_BoyWithBrainXSmall And here the world turns upside down. 

In the absence of test-driven development, I’d think about this a lot and make all sorts of decisions before writing a line of code. I’d decide, no doubt that the ItemsViewModel needs to provide a collection of Item objects to serve as the ItemsSource, and thus the Items property of the class would be a collection of Item objects. 

Since CurrentItem is bound to the SelectedItem, I’d try to figure out if that is a property with its backing value held in the ItemsViewModel or in, for example, the state object that is shared by all the ViewModel classes.

You can do all that, and you can make unit tests for it, but test-driven development works differently.  In TDD you make much smaller design decisions, write much smaller bits of code, and create a rapid cycle of three steps:

  1. Determine one key thing  you believe the ItemsViewModel class must provide and write a test asserting that it does provide that. Run that test. It will fail.
  2. Write just enough code in ItemsViewModel to make the test pass. No stop, just enough, no more. Good. Run the test again, it should pass.
  3. Repeat*
* Old joke: a programmer  was stuck in the shower because the shampoo bottle said “apply, lather, rinse, repeat.”

 

The Discipline of TDD

Yes, it is infuriating to write the simplest thing when you believe you already know more. But a funny thing happens… you find that you are never lost, confused, befuddled or even in need of a debugger. You only did a little bit and the test either passes or it fails. If it fails, it is trivial to figure out what is wrong; after all you probably only wrote a couple lines of code.

Creating The Test in Visual Studio

HVPTests To get started, I’ll add a project to the SilverlightHVP solution of type Test Project and I’ll name it SilverlightHVP.Tests.

Visual Studio will add the project, add a file UnitTest1.cs and add two new files to the solution: Local.testsettings and TraceAndTestImpact.testsettings. It also adds a reference to Microsoft.VisualStudio.Quality.

Tools.UnitTestFramework. Once the project is added to the solution, I can right click on the new project and choose Add->NewTest, bringing up the Add New Test dialog box.  I recommend experimenting with each of the tests offered, but for now, I’ll just  AddNewTest choose Basic Unit Test and name it ItemsViewModelUnitTests. 

Visual Studio will create the class and a first test method, and adorn each with the appropriate attributes.  Delete TestMethod1 and it is time to write (and name!) our first test method.

The First Test

So; what is the very least we think the ItemsViewModel must do? Let's return to the ViewModel and note that the ListBox.ItemsSource property is binding to a ItemsViewModel property named Items. 

A quick look at the Silverlight documentation reveals that the ListBox.ItemsSource property is of type IEnumerable. That is, a collection. We don’t yet know what is in the collection, but we do know that the ItemsViewModel must have a property Items that returns an IEnumerable of something. Let’s create a test for that.

So what is the test actually testing for? The minimum is that the Items property, an IEnumerable<object> (that is, an enumerable list of something) is not null.

Our naming convention is to identify the thing we are testing and what should be true. The name, therefore, is ItemsProperty_ShouldNotBeNull.

 [TestMethod]
 public void ItemsProperty_ShouldNotBeNull()
 {
 }

Creating the body of the test requires testing the property of an ItemsViewModel instance; so the first line of the test will create the beast. 

CreateInstanceOfIVM

When we attempt to make an instance, we immediately get the feedback that something is amiss; no surprise; the class doesn’t exist yet. Visual Studio is ever-helpful, however, and it offers to generate the classs for you. Let’s have it do  that.  Hey! Presto! a new file ItemsViewModel.cs is generated in the test project. That’s not where we’ll want it, but for now it is just fine.

Our first test line no longer shows an error, and oh, by the way, creating this line was a forcing function on the design: we needed a class of type ItemsViewModel.   ItemsProperty Similarly, we now will attempt to retrieve the Items property as an enumerable, which of courses will generate both an error (no such property) and an offer to create the property for you.  Once again we’ll let Visual Studio generate the needed stub.  With that, we can test that Items is not null:

TestFailed

Place your cursor on the name of the test method and press Control-R, T; this will run this one test and display the results in a window, as shown.

Our dreams and ambitions are fulfilled; the test failed as we expected it to!  Great.

(click on images for full size)

Making The Code Pass The Test

Now we write just enough code for the test to pass. Watch it! Just enough and no more.

 class ItemsViewModel
 {
    public IEnumerable<object> Items 
    {
       get { return new List<Object>(); }
       set { }
    }
 }

Since I can’t instantiate an interface, I chose a very simple class that implements IEnumerable. Notice that I’ve not populated the list; all I’ve done is write enough code to satisfy the test. This is pedantic, rigid, annoying and incredibly useful. The design emerges based on the needs, the needs are expressed by the tests, and the code never goes beyond the (thought through and tested) requirements. 

A real though not driving benefit is that the tests become both insurance and documentation; and unlike other documentation, as long as you “rigidly” make sure you run all the tests every time you make any change, the documentation never goes out of date.

The Second Test

At this point we know that we want more than for the property to be non-null; we want it to have items. Let’s test for that:

 

      [TestMethod]
      public void ItemsProperty_ShouldNotBeNull()
      {
         var itemsViewModel = new ItemsViewModel();
         IEnumerable<object> items = itemsViewModel.Items;
         Assert.IsNotNull( items );
      }

      [TestMethod]
      public void ItemsProperty_ShouldReturnListOfObjects()
      {
         var itemsViewModel = new ItemsViewModel();
         IEnumerable<object> items = itemsViewModel.Items;
         Assert.AreNotEqual<int>( 0, items.Count<object>() );
      }

Running Two Tests

You can run the second test just as you ran the first, but we really want to make sure that it is wicked easy to run all the tests every time. Click in the class, but not on either test, and press Ctrl-R, T.  This causes both tests to be run, and sure enough the first test (ShouldNotBeNull) passes, but the new test fails.

Once again, we write just enough code to make this pass.

 class ItemsViewModel
 {
    public IEnumerable<object> Items 
    {
       get { return new List<Object>() { new Object() }; }
       set { }
    }
 }

And running the tests again, both pass. And so it goes. Step by tiny step. 

Factor Out Common Code In Tests?

Most of you are probably starting to itch terribly, as you look at the two tests and note that the first two (of three!) lines are identical.  Yikes! Somebody factor that out, quick!

And we will, but before we do, let me note that this is not a given; in fact doing so is somewhat controversial.  First, how to do it, then the controversy.

[TestClass]
public class ItemsViewModelTests
{
   private IEnumerable<object> items;
   [TestInitialize]
   public void ItemsProperty_SetUp()
   {
      var itemsViewModel = new ItemsViewModel();
      items = itemsViewModel.Items;
   }

   [TestMethod]
   public void ItemsProperty_ShouldNotBeNull()
   {
      Assert.IsNotNull( items );
   }

   [TestMethod]
   public void ItemsProperty_ShouldReturnListOfObjects()
   {
      Assert.AreNotEqual<int>( 0, items.Count<object>() );
   }

   [TestCleanup]
   public void ItemsProperty_CleanUp()
   {
      items = null;
   }
}

The trick here is that the testing framework will run each test in the following sequence:

  • If there is a method marked [TestInitialize] run that first
  • Run the test method
  • If there is a method marked [TestCleanup] run that last

The controversy is this: you do not want any dependencies between tests. It can never be allowed that running one test affects another. That is, for example, ShouldReturnListOfObjects should return the same results if it is run before or after ShouldNotBeNull or even if ShouldNotBeNull is not run at all.

One way to protect yourself is to have each test be fully self-contained. A second way is to ensure that CleanUp eliminates any side-effects. 

Or Not…

James Newkirk, one of the brighter lights in the field, author of nUnit and xUnit, explains in some detail why he opposes using Setup and Cleanup (TearDown) methods.

I’ve taken the liberty® of quoting one of the key paragraphs:

The problem that I have with SetUp in this case is twofold. The first and primary complaint is that when I am reading each test I have to glance up to BeforeTest() to see the values that are being used in the test. Worse yet if there was a TearDown method I would need to look in 3 methods. The second issue is that BeforeTest() initializes member variables for all 3 tests which complicates BeforeTest() and makes it violate the single responsibility pattern. 

Brad Wilson, Newkirk’s partner in crime, elucidates four objections to the use of setup and teardown, which I summarize here,

  • You can’t tell what the test is doing without looking at the setup, which can be far away in the file when you have a number of tests
  • If the setup or teardown throws an exception it can be very confusing as to what is wrong
  • There is only one setup and teardown per set of tests, and that presumes every test needs the same setup; a dangerous and misleading assumption that makes the test harder to understand.
  • It is too easy to have the setup code change the environment for tests run later in the series; breaking the rule that tests are independent.

==========

I am incredibly grateful to Abby Fichtner for helping me past the initial hurdles in Test Driven Development (though she may argue I’m not yet past them!). 

She recommended to me, and I recommend to you this excellent tutorial by Jason Olson from Channel 9 (where you can now watch Silverlight TV)

 

jessesig

This work is licensed under a Creative Commons license.
Published Tuesday, February 09, 2010 10:42 PM by jesseliberty

Comments

# Social comments and analytics for this post

This post was mentioned on Twitter by JesseLiberty: New blog post: http://tinyurl.com/yaw9vw6 - Silverlight Test Driven Development Part II - A Walkthrough.

Tuesday, February 09, 2010 11:01 PM by uberVU - social comments

# re: Silverlight Test Driven Development – Part II

YaY! So exciting to see this coming along. :-) And very cool getting to try out the new TDD support built into Visual Studio 2010 on a real project!

Tuesday, February 09, 2010 11:06 PM by HackerChick

# Daily tech links for .net and related technologies - Feb 10-12, 2010

Daily tech links for .net and related technologies - Feb 10-12, 2010 Web Development 'Unobtrusive' asynchronous

Wednesday, February 10, 2010 2:00 AM by Sanjeev Agarwal

# Silverlight Test Driven Development – Part II - Jesse Liberty

Thank you for submitting this cool story - Trackback from DotNetShoutout

Wednesday, February 10, 2010 3:22 AM by DotNetShoutout

# MLB 10: The Show Preview with Joe Mauer in High Definition ??? G4tv.com | Minnesota Twins MLB Announcer

Pingback from  MLB 10: The Show Preview with Joe Mauer in High Definition ??? G4tv.com | Minnesota Twins MLB Announcer

# The Test of Testing

Pingback from  The Test of Testing

Wednesday, February 10, 2010 10:33 PM by The Test of Testing

# re: Silverlight Test Driven Development – Part II

Dilemma between writing test code like real code (refactoring common code to test setup methods etc) is still a valid one and i find myself often changing opinion on this...

Very good post!

Planing to write any followups on this subject?

Friday, February 12, 2010 3:33 PM by spavkov

# re: Silverlight Test Driven Development – Part II

The more TDD you do, the more of your codebase will be test code. If you were to write an entire application from scratch using TDD ( and I recommend that you do ), then it's quite possible, even likely, that at least 50% of your code will be test code. This will include unit tests, integration tests, user journeys and so on.

This is why I would advocate applying exactly the same principles of software quality to your test code as you do to your production code. Over the lifetime of your application, your production code will change, and your test code must change with it - or you defeat the object of creating the test code in the first place. If you haven't applied equally strict principles of software engineering to your test code then this will quickly become a maintenance nightmare.

Just my 2 cents.

Andrew.

Monday, February 15, 2010 5:51 AM by planetmarshall

# Links (2/16/2010) &laquo; Steve Pietrek-Everything SharePoint/Silverlight

Pingback from  Links (2/16/2010) &laquo; Steve Pietrek-Everything SharePoint/Silverlight