Cory Foy

Wednesday, October 31, 2007

TDD of a WinForm app - Part 2 - Presenting the Account Data

<= Part 1 | Series Home | Part 3 =>

In our last section, we put together a list of accounts and made sure we could search them in the way our customer wanted. To recap, our customer needed a way to enter in accounts and be able to search them by phone number or name. From a UI perspective, this should be accomplished using one field. Now that we have the searching business logic down, let's get the UI working so we can show our customer.

 

UI, meet Business Layer

The sketch for the UI is basically a text field with a button next to it to search and a grid underneath the buttons and textbox to show the results. When the app first starts, the grid should show the list of every customer, sorted by name.

To build a TDDable UI, I typically use a slightly modified form of Fowler's Presentation Model where I define the exposed actions as an interface, and use the UI screen as a form of Data Transfer Object. To explain, it will probably be best to get writing tests, ey?

The easiest thing to start with is to make sure that the events are getting hooked up correctly. While .NET controls expose a set of events (like Click), I prefer to use event names that are consistent with the domain model. So, for example, the first thing we want to check is that the presenter registers itself as a listener for the SearchCustomersRequested event. This will be the event the UI fires when someone clicks on the button after entering search criteria. I create two new projects - ServiceTrackerPresenter and ServiceTrackerPresenterTests, create a test class called AccountPresenterTests, and write the following test:

[TestMethod]
public void PresenterRegistersAsListenerForSearchCustomersEvent()
{
  IAccountView view = new StubAccountView();
  AccountPresenter presenter = new AccountPresenter(view);
  int expectedListenersCount = 1;
  int actualListenersCount =
    ((StubAccountView)view)
      .SearchCustomersRequestedListenerCount;
  Assert.AreEqual(expectedListenersCount, actualListenersCount);
}

Something I'm doing here is using the interface declaration, and only casting it to the Stub implementation when I need something special from the Stub that the UI won't expose. I've found that keeps my code honest and prevents mistakes from putting something in the stub that isn't in the UI or interface.

So let's get the test compiling. In ServiceAccountPresenter I create two classes - IAccountView and AccountPresenter. The only thing I put in either of them is a constructor in AccountPresenter which accepts an IAccountView. Then, in my AccountPresenterTests.cs, I create the StubAccountView class, having it implement IAccountView. Inside it, I put a property called SearchCustomersRequestedListenerCount, and set it to return 1. Viola! Tests green.

Except that isn't really what we want  here. So I modify the test slightly to make sure it is actually the presenter registering the event:

[TestMethod]
public void PresenterRegistersAsListenerForSearchCustomersEvent()
{
  IAccountView view = new StubAccountView();
  Assert.AreEqual(0, ((StubAccountView)view)
    .SearchCustomersRequestedListenerCount);

  AccountPresenter presenter = new AccountPresenter(view);
  int expectedListenersCount = 1;
  int actualListenersCount =
    ((StubAccountView)view)
      .SearchCustomersRequestedListenerCount;
  Assert.AreEqual(expectedListenersCount, actualListenersCount);
}

Ok, now the test fails. So let's setup our event subscription. First, we need to define the event and delegate in our interface:

namespace ServiceTrackerPresenter
{
  public delegate void AccountViewEventDelegate(
    object sender, IAccountView view);

  public interface IAccountView
  {
    event AccountViewEventDelegate SearchCustomersRequested;
  }
}

And modify our Stub to implement this:

class StubAccountView : IAccountView
{
  public event AccountViewEventDelegate 
    SearchCustomersRequested;

  public int SearchCustomersRequestedListenerCount
  {
    get
    {
      if (SearchCustomersRequested != null)
      {
        return SearchCustomersRequested
          .GetInvocationList().Count();
      }
      return 0;
    }
  }
}

Which gives us a still failing test. Now we just need to modify the behavior of the Presenter:

public class AccountPresenter
{
  public AccountPresenter(IAccountView view)
  {
    view.SearchCustomersRequested += new
      AccountViewEventDelegate
        (view_SearchCustomersRequested);
  }

  void view_SearchCustomersRequested(
    object sender, IAccountView view)
  {
  }
}

We aren't doing much with it, but our test isn't asking us to. This passes our test, and we run all of our tests just to make sure everything is still looking fine (it is!).

 

Displaying Accounts using DataBinding

So we now know we can respond to someone clicking the button. It isn't much, but sometimes it can be hard to figure out what the first test to write is, and for some reason that particular behavior was standing out in my mind. That's because the real first behavior we have to deal with is displaying Accounts on the page.

Looking again at Fowler's Presentation model, the form pulls the information from the model. In other words, he has a LoadFromPresentationModel() method in the view which sets up everything in the page. I prefer to expose everything that needs to be set in the interface and let the Presenter set the fields.

So it goes with displaying the Accounts. Because of .NET's rich DataBinding capability, it will be unlikely that we'd want write our own method to loop through the data and insert it into a grid. We could certainly do that - expose an object in our interface which has an Add() method, and in our presenter loop over the Accounts and call that method. However, I prefer to just use databinding.

So, the next thing we need to specify is that when the form loads, we want to do something, and that something we want to do is set the DataSource of our Domain object that will display the accounts. First, we'll write a test similar to our last one:

[TestMethod]
public void PresenterRegistersAsListenerForLoadEvent()
{
  IAccountView view = new StubAccountView();
  Assert.AreEqual(0, ((StubAccountView)
    view).AccountViewLoadListenerCount);
  AccountPresenter presenter = new AccountPresenter(view);
  int expectedListenersCount = 1;
  int actualListenersCount =
    ((StubAccountView)view).AccountViewLoadListenerCount;
  Assert.AreEqual(expectedListenersCount, actualListenersCount);
}

And a property to our Stub which returns 0. With the failing test in place, we add the event to our interface, modify our Stub to return the InvocationList count, and then modify our presenter to register as a listener. Green test!

The next thing we want to do is make sure that the DataSource is getting set when the form loads. But the DataSource of what? Obviously we are going to have a Grid of some sort (likely a DataGrid), but to do that, we'd have to have a DataGrid property in our interface, and something about that doesn't sit right with me.

That leaves us with two options. The first is to write our own interface for data bound controls which exposes the DataSource property, and then have the UIs create a wrapper around the real control which implements the interface (man how I wish .NET used Duck Typing). The second is to have a property of the form itself be set, and rely on the UI authors to do the right thing when they are implementing the form. Some part of me wants to be more deterministic then that, so we'll try the first method.

Here's our test:

[TestMethod]
public void AccountGridDataSourceSetDuringViewLoad()
{
  IAccountView view = new StubAccountView();
  AccountPresenter presenter = new AccountPresenter(view);
  Assert.IsNull(view.AccountGrid.DataSource);
  ((StubAccountView)view).FireAccountViewLoadEvent();
  Assert.IsNotNull(view.AccountGrid.DataSource);
}

We'll make AccountGrid an IAccountGrid interface in our ServiceTrackerPresenter with a DataSource property:

public interface IAccountGrid
{
  object DataSource { get; set; }
}

And add AccountGrid to our IAccountView interface. Notice that we are only requiring a get method - it will be unlikely our Presenter will replace an entire DataGrid with a new one, and we can always come back and tweak this later if need be:

IAccountGrid AccountGrid { get; }

Now we're getting a compiler error that StubAccountView doesn't implement the IAccountView, so we'll add AccountGrid to it. Now we're getting a compiler error that FireAccountViewLoadEvent doesn't exist in StubAccountView. FireAccountViewLoadEvent is a helper method to raise events that would typically be raised by the UI - in this case, AccountViewLoad. The method is easy to implement:

public void FireAccountViewLoadEvent()
{
  if(AccountViewLoad != null)
    AccountViewLoad(this, this);
}

Now the compiler is happy. Let's see about our tests. It fails that AccountGrid is null (which throws a NullReferenceException when we try to access DataSource during the test). What we need is a sensing object to tell when DataSource has been set. So in our AccountPresenterTests we'll create a StubAccountGrid which implements IAccountGrid and just uses an instance variable to hold DataSource:

class StubAccountGrid : IAccountGrid
{
  private object dataSource;

  public object DataSource
  {
    get { return dataSource; }
    set { dataSource = value; }
  }
}

And in our StubAccountView we'll use an instance of this class to track what our presenter is doing:

private IAccountGrid accountGrid;

public StubAccountView()
{
  accountGrid = new StubAccountGrid();
}

public IAccountGrid AccountGrid
{
  get { return accountGrid; }
}

Ok, the compiler is happy, and our tests are now failing that the Assert.IsNotNull failed. So let's fix that by modifying our presenter to set the data source on load:

void view_AccountViewLoad(object sender, IAccountView view)
{
  view.AccountGrid.DataSource = new Accounts();
}

And we have a green test! At this point, some of you may be thinking that setting the DataSource to a new Accounts collection is useless - at best we'll have a UI that has an empty grid. And you'd be correct - for now. As we write more tests, we should flesh out the DataSource semantics. But to do that, we need a failing test.

 

Hooking the grid to the DataSource

In fact, that sounds like a good next round of functionality. We have a grid, and a way for a user to tell us that they want to submit criteria, but we don't have the two hooked up. What we need to do at this point is build the hooks between the user submitting the search criteria, and what gets set to the grid.

So perhaps our next test can look like...Wait a second. Before we start coding the next test, we have some serious duplication happening in our tests. They all seem to start with:

IAccountView = new StubAccountView();
AccountPresenter presenter = new AccountPresenter(view);

However, for our first two tests in this class, we have an assert between the view and presenter instatiations. So we'll leave it for now, but be mindful of the duplication as we go.

So, the next test for hooking up search with the grid can look something like:

[TestMethod]
public void GridDSSetToUpdatedAccountsWhenSearchEventFired()
{
  IAccountView view = new StubAccountView();
  AccountPresenter presenter = new AccountPresenter(view);
  ((StubAccountView)view).FireAccountViewLoadEvent();
  view.SearchCriteria = "813-555-1234";
  ((StubAccountView)view)
    .FireSearchCustomersRequestedEvent();
  Account expectedAccount =
    new Account("Test User", "813-555-1234");
  Accounts accounts =
    view.AccountGrid.DataSource as Accounts;
  Assert.AreEqual(expectedAccount, accounts[0]);
}

I don't like all that setup just to get a Loaded view, but we'll tackle that if we need another test. For now we are getting a compiler exception that SearchCriteria doesn't exist in view, and that FireSearchCustomersRequestedEvent doesn't exist in our Stub. So we'll add a SearchCriteria property which returns a string to our interface, and modify our Stub class to add this property and the new event fire method:

public void FireSearchCustomersRequestedEvent()
{
  if (SearchCustomersRequested != null)
    SearchCustomersRequested(this, this);
}

private string searchCriteria;

public string SearchCriteria
{
  get { return searchCriteria; }
  set { searchCriteria = value; }
}

Now everything compiles. Our test fails with an index out of range exception - our DataSource is being set to a new instance of Accounts in our presenter. Let's modify that to hard code some accounts for now. First we'll modify our listener to call a method to get the Accounts:

void view_AccountViewLoad(object sender, IAccountView view)
{
  view.AccountGrid.DataSource = LoadAccounts();
}

private Accounts LoadAccounts()
{
  Accounts accounts = new Accounts();
  return accounts;
}

And rerun our tests to make sure we didn't break anything. We didn't, so let's finish the LoadAccounts method:

private Accounts LoadAccounts()
{
  Accounts accounts = new Accounts();
  accounts.Add(new Account("Test User", "813-555-1234"));
  accounts.Add(new Account("Justin Example", "813-555-1212"));
  return accounts;
}

Now our test is failing that it expected an Account, but got an Account. However, that brings up a question - do two accounts with the same information (Name, PhoneNumber) equal each other? Looking at the Account.cs class, we didn't override Equals, so we should probably do that first. We comment out the GridDS test we are working on, and create a new AccountTests.cs class in the ServiceTrackerLogicTests project. Our first test looks like:

[TestMethod]
public void TwoAccountsWithMatchingNameAndPhoneAreEqual()
{
  Account account1 = new Account("Test User", "813-555-1234");
  Account account2 = new Account("Test User", "813-555-1234");
  Assert.AreEqual(account1, account2);
}

Which fails. So we don't have that implemented. Let's go into Account.cs and implement an equals method:

public override bool Equals(object obj)
{
  if (obj is Account)
  {
    Account a = obj as Account;
    return a.Name == this.Name
      && a.PhoneNumber == this.PhoneNumber;
  }
  return false;
}

Which passes our test. We'll also override GetHashCode and have it return the base.GetHashCode for now.

Back to our Grid. We uncomment our test and run it. This time it passes! WooHoo! Or...is it? We know that we didn't hook up the events yet, so let's add another Assert in there:

[TestMethod]
public void GridDSSetToUpdatedAccountsWhenSearchEventFired()
{
  IAccountView view = new StubAccountView();
  AccountPresenter presenter = new AccountPresenter(view);
  ((StubAccountView)view).FireAccountViewLoadEvent();
  view.SearchCriteria = "813-555-1234";
  ((StubAccountView)view)
    .FireSearchCustomersRequestedEvent();
  Account expectedAccount =
    new Account("Test User", "813-555-1234");
  Accounts accounts =
    view.AccountGrid.DataSource as Accounts;
  Assert.AreEqual(1, accounts.Count());
  Assert.AreEqual(expectedAccount, accounts[0]);
}

This fails that there are two elements in our Accounts collection - it just happened that the item that was the first in the list was the one we were looking for. That's why it is always important to sanity check your objects as well.

On to getting our test passing. We modify the following method in AccountPresenter:

void view_SearchCustomersRequested(
  object sender, IAccountView view)
{
  Accounts accounts = LoadAccounts();
  accounts = accounts.FindAccount(view.SearchCriteria);
  view.AccountGrid.DataSource = accounts;
}

Which passes our tests, meaning we're almost there. At this point we have our datagrid being loaded with all of the accounts on load, and a user can enter search criteria to filter the grid, and the grid data is filtered by the criteria.

 

Show me the Data! On a User Interface!

At least, our tests say we do. Up till now, we haven't touched a UI. For the last part of this section, we're going to hook up the UI and run through a manual test. Later on we'll work on automating the UI tests, but for now we have a demo to do.

In our ServiceTracker project, we have a Form1.cs file that's been sitting there since we started. Let's rename it to MainForm.cs and look at the code. We see that it has a constructor, but little else. Let's open the form in the designer and drag a Button, a TextBox and a DataGrid onto the form, naming them SubmitSearch, SearchCriteriaText and AccountsGrid:

Now to start hooking these things up. Go to the code for the form, and have MainForm.cs implement IAccountView. Once you have that wired up, you can right-click on IAccountView and choose Implement Interface in Visual Studio:

This gives us our two events, and the two properties. The first and easiest to implement is SearchCriteria:

public string SearchCriteria
{
  get { return SearchCriteriaText.Text; }
  set { SearchCriteriaText.Text = value; }
}

In other words, we are delegating the calls directly to the UI elements. Our code behind is basically just a mapper of our domain events to the UI elements that implement them. We do similar things with the two events:

public event AccountViewEventDelegate  
  SearchCustomersRequested;
public event AccountViewEventDelegate AccountViewLoad;

private void MainForm_Load(object sender, EventArgs e)
{
  if (AccountViewLoad != null)
    AccountViewLoad(this, this);
}

private void SubmitSearch_Click(object sender, EventArgs e)
{
  if (SearchCustomersRequested != null)
    SearchCustomersRequested(this, this);
}

Again, mapping the Form.Load and Button.Click events to our domain events. The only thing left is our AccountGrid. We could do this:

public IAccountGrid AccountGrid
{
  get { return AccountsGrid; }
}

But the compiler complains that AccountsGrid doesn't implicitly implement IAccountGrid. We could make it explicit:

public IAccountGrid AccountGrid
{
  get { return AccountsGrid as IAccountGrid; }
}

But while the compiler is happy, that would throw a runtime exception. In language like Ruby, this would work because of the principle of Duck Typing, but we have to find a workaround.

In the spirit of DTSTTCPW, we'll just create a wrapper class in our MainForm.cs class:

class AccountsDataGridWrapper : IAccountGrid
{
  DataGridView dgv;

  public AccountsDataGridWrapper(DataGridView dgview)
  {
    dgv = dgview;
  }

  public object DataSource
  {
    get { return dgv.DataSource; }
    set { dgv.DataSource = value; }
  }
}

And modify our AccountGrid implementation to use the Wrapper:

public IAccountGrid AccountGrid
{
  get
  {
    return new AccountsDataGridWrapper(AccountsGrid);
  }
}

Now hit F5 and...

Viola! Our UI is hooked up to the presenter with no business logic needing to reside in it. Searching works as well:

Now let's go demo this baby! We still have a lot of work to do besides automating the UI test - we have the rest of the business logic to go. But we're moving closer, and we have our tests to support us. And best of all, our customer is seeing real progress at the same time.

Next time we'll discuss how the demo goes and what part we are going to tackle next.

TDD of a WinForm app - Part 1 - Searching for Accounts

<= Part 0 | Series Home | Part 2 =>

As I stated in the introduction, we're building an app for my father-in-law who runs a small appliance repair business. His biggest pain point is that he captures all of his customer data on paper invoices, and so has no easy way to look up customer information. So his first need was a way to track the customers that he went to, and provide a way to search them.

 

Working with Accounts

Providing a searchable list of customers seemed like a good place to start. So I fired up Visual Studio and created a new WinForm app called ServiceTracker. I then added a class library called ServiceTrackerLogic and a Test application called ServiceTrackerLogicTests (I'm using MSTest for the tests). I knew we would capture the customer information as an Account, so it seemed logical to work on Accounts. I created a class in my Tests called AccountsTests.cs and wrote the first test:

[TestMethod]
public void AddingAccountToAccountsIncrementsListCount()
{
  Account account = new Account("Test User", "813-555-1234");
  Accounts accounts = new Accounts();
  accounts.Add(account);
  Assert.AreEqual(1, accounts.Count);
}

I added Account.cs and Accounts.cs classes to my Logic project, and added a constructor for Account.cs to take in two strings. For Accounts.cs I added a property called Count which returned 1 and an Add method which takes an Account. All green. I then added a second test:

[TestMethod]
public void AddingAccountToAccountsAddsAccountToList()
{
  Account account = new Account("Test User", "813-555-1234");
  Accounts accounts = new Accounts();
  accounts.Add(account);
  Assert.AreEqual(account, accounts[0]);
}

This fails that the indexer can't be used. Since I plan on working with a basic list anyway, I have Accounts.cs subclass List<Account>:

public class Accounts : List<Account>

I then delete the Add method and Count propery, since they are provided by the base List, and run the tests. All green.

 

Searching Accounts

The next thing I need to do is search the list for a name or phone number. Ideally I want one method which can do either, since the User Interface won't care. That leads to our next test:

[TestMethod]
public void SearchingByFullNameReturnsMatchingAccount()
{
  Account account = new Account("Test User", "813-555-1234");
  Accounts accounts = new Accounts();
  accounts.Add(account);
  Accounts foundAccounts = accounts.FindAccount("Test User");
  Assert.AreEqual(1, foundAccounts.Count);
  Assert.AreEqual(account, foundAccounts[0]);
}

To compile, I add a FindAccount method to Accounts which takes in a string and returns Accounts. We now have a failing test, so let's make it pass:

public Accounts FindAccount(string searchCriteria)
{
  return this;
}

It's sad that I can be just as annoying of a pair with myself. Fine, green test. So let's get a better one:

[TestMethod]
public void SearchingByFullNameWhenMultipleAccountsReturnsOnlyMatchingAccount()
{
  Account account = new Account("Test User", "813-555-1234");
  Account account2 =
    new Account("Justin Example", "813-555-1212");
  Accounts accounts = new Accounts();
  accounts.Add(account);
  accounts.Add(account2);
  Accounts foundAccounts = accounts.FindAccount("Test User");
  Assert.AreEqual(1, foundAccounts.Count);
  Assert.AreEqual(account, foundAccounts[0]);
}

Aha! Failing test. Now we have to do something about it. While we could loop through all of the members in Accounts, the List<T> class provides a method called FindAll which takes in a delegate to look through each object. So we modify our FindAccount method:

public Accounts FindAccount(string searchCriteria)
{
  Accounts foundAccounts = this.FindAll(MatchesFullName);
  return foundAccounts;
}

Well, at least we want to do that. Turns out FindAll returns a List<Account>, when we are dealing with Accounts. So we need to modify Accounts to use the List<T> constructor which takes in an IEnumerable so we can change our method to:

public Accounts FindAccount(string searchCriteria)
{
  Accounts foundAccounts =
    new Accounts(this.FindAll(MatchesFullName));
  return foundAccounts;
}

MatchesFullName is the other method which we added. It takes in an Account and returns true if that account matches the criteria. Of course, it doesn't show how to get the criteria to the search method, but that's Ok, because we can just do:

private bool MatchesFullName(Account account)
{
  return account.Name.Contains("Test User");
}

Which doesn't compile because we need to add accessors for Name and PhoneNumber to our Account.cs class and modify the Account constructor to set them. Ok, with that done, we now have green tests. Looking at our classes, our production code doesn't quite need any refactoring, but our tests could. Let's pull up some of the commonly used variables:

private Account testAccountTestUser;
private Accounts initializedAccounts;

[TestInitialize]
public void Setup()
{
  testAccountTestUser =
    new Account("Test User", "813-555-1234");
  initializedAccounts = new Accounts();
  initializedAccounts.Add(testAccountTestUser);
}

Ok, now we said early we want one method to search both name and phone number. So let's write a test which expresses that:

[TestMethod]
public void SearchingByFullPhoneNumberReturnsMatchingAccount()
{
  Accounts foundAccounts =
    initializedAccounts.FindAccount("813-555-1234");
  Assert.AreEqual(1, foundAccounts.Count);
  Assert.AreEqual(testAccountTestUser, foundAccounts[0]);
}

Well, this passes. Looks like I have to break out a deeper test:

[TestMethod]
public void SearchingByFullPhoneWhenMultipleAccountsReturnsOnlyMatchingAccount()
{
  Account account2 =
    new Account("Justin Example", "813-555-1212");
  initializedAccounts.Add(account2);
  Accounts foundAccounts =
    initializedAccounts.FindAccount("813-555-1234");
  Assert.AreEqual(1, foundAccounts.Count);
  Assert.AreEqual(testAccountTestUser, foundAccounts[0]);
}

There. A failing test. So now we have to tackle how to pass a parameter to our search method. The easiest way seems to be to have an instance variable we set with the search criteria, and then use that in the search method. So we modify our Accounts.cs class to:

private string searchCriteria;

public Accounts FindAccounts(string criteria)
{
  searchCriteria = criteria;
  Accounts foundAccounts = new Accounts(
    this.FindAll(MatchesFullNameOrPhone));
  searchCriteria = String.Empty;
}

My internal pair chides me for the extra code, but I can't bring myself not to put it in there. We can now modify our old MatchesFullName to:

private bool MatchesFullNameOrPhone(Account account)
{
  return account.Name == searchCriteria
    || account.PhoneNumber == searchCriteria;
}

Which passes our tests.  

 

Searching partial matches

Ok, almost there. The last thing we need to write behavior logic for is searching partial names and phone numbers:

[TestMethod]
public void SearchingByCharactersAtBeginningOfNameFindsAccount()
{
  Accounts foundAccounts =
    initializedAccounts.FindAccount("Tes");
  Assert.AreEqual(1, foundAccounts.Count);
  Assert.AreEqual(testAccountTestUser, foundAccounts[0]);
}

Which fails. So we modify our Matches to add an or condition for account.Name.StartsWith(searchCriteria). Green test. We then write similar tests for text at the end of the name (passing with EndsWith) and for text in the middle of the name (passing with Contains). We then do the same thing for Phone Number, and after a little refactoring we end up with:

private bool MatchesFullNameOrPhone(Account account)
{
  return account.Name.Contains(searchCriteria)
    || account.PhoneNumber.Contains(searchCriteria);
}

Nice and clean. And all of our tests are passing now too. So our first requirement is out of the way - we can add Accounts and search for them based on name or phone number. In the next part, we'll actually create a user interface for searching and displaying the Accounts so our customer can start to see real value from the application.

Monday, October 29, 2007

TDD of a WinForm app - Part 0 - Introduction

When you do any kind of computer work - or really do anything where you have a specific skill - you get asked to do some "family work". This is usually some family member that needs something done to their computer, or something else, and wants you to do it for them.

In our family, I jumped at the chance to help out my father-in-law. He runs a small business doing appliance repair and needed a way to track the calls he goes on. The app is pretty straightforward:

  • A customer entry piece for entering the name, address and phone number of customers he visits (and a way to search for a customer)
  • An invoice piece to enter the work he does for a customer
  • A basic reporting piece to show a list of the customers that haven't paid yet

Oftentimes in the course of showing Test Driven Development, we resort to things like the bowling game to show the basic concepts. I thought it would be fun to document the building of the application using TDD since it uses two things that are commonly cited as being very difficult to TDD - WinForms and Databases.

The series will be done ala Ron Jeffries style - all the warts will be exposed. You'll get to see my mistakes and my successes. And using Test-Driven Development, we'll hopefully see a good app built in a relatively short amount of time.

As parts get finished in the series, I'll link to them from the bottom of here. See you soon!

Thursday, October 25, 2007

Welcome to Charlotte Elizabeth Foy

UPDATE: More information and pictures at our family web site

Yesterday morning at 7:49am I had the priviledge of watching what makes anything that men do look silly - my wife giving birth to our new daughter. Weighing in at 9lbs 1oz (!) she made us labor for just over 18 hours.


Little Charlotte at just a few minutes old

Mom and baby are doing great, and Annabelle (16 months) is only slightly interested in her sister when she cries (which isn't much)

 
Charlotte with her sister Annabelle and Grandma Gail

We've had very little sleep over the past few days, but I hope to get more of the pictures up on our website soon.

Thursday, October 18, 2007

How to Test something that closes the stream?

A question came up on the TDD mailing list earlier this week - how do you deal with an application that closes your memory stream before you have a chance to assert what is going on with it?

The solution the poster did was to have a byte array and have a stream on that. But you actually have a couple of choices:

Strings - If your app is writing strings (say logging messages) then you can use System.IO.StringWriter. It uses a StringBuilder as a backing store which will still be around even if your app closes the stream.

Other Streams - but if you really need to compare streams, then you have the System.IO.MemoryStream. However, you can't see what is in it once it has been closed. For example:

static void Main(string[] args)
{
  using(MemoryStream stream = new MemoryStream())
  {
    WriteToStream(stream);
    stream.Position = 0;
    byte b1 = (byte)stream.ReadByte();
    Console.WriteLine(b1.ToString());
  }
}

static void WriteToStream(Stream stream)
{
  byte b = 1;
  stream.WriteByte(b);
}

outputs "1" as expected. However, if we closed the stream in WriteToStream, we get an error:

static void WriteToStream(Stream stream)
{
  byte b = 1;
  stream.WriteByte(b);
  stream.Close();
}

As we saw above, one solution would be to use a byte array or something else that will still be around. However, another choice is to fake out the method closing the Stream by deriving a class:

class FakeCloseMemoryStream : MemoryStream
{
  public FakeCloseMemoryStream() : base() { }
  public bool closeWasCalled = false;
  public override void Close()
  {
    closeWasCalled = true;
  }
  public void ReallyClose()
  {
    base.Close();
  }
}

static void Main(string[] args)
{
  FakeCloseMemoryStream stream =
    new FakeCloseMemoryStream();
  WriteToStream(stream);
  stream.Position = 0;
  byte b1 = (byte)ReadByte();
  Console.WriteLine(b1.ToString());
  stream.ReallyClose();
}

Which gets us nicely past it. Of course, your requirements may not allow for this, but when you need something to stand in, remember that subclassing can be your friend.

Test Driven Development with Visual Studio for Database Professionals posted on InfoQ

Just a quick note that a follow-up article to my TDD with DBPro article was posted on InfoQ. Feel free to go check it out!

Test Driven Development with Visual Studio for Database Professionals

Tuesday, October 16, 2007

Discovering who is holding a reference to a file with Process Monitor

A common question I see come up is someone working with ASP.NET tries to push a new deployment to their server but then sees errors from aspnet_compiler or during JIT which says that the compiler can not access certain files in either the web root or the Temporary ASP.NET Files.

Almost always this is caused by either not shutting down IIS before pushing large updates, or by not excluding your web root and Temporary ASP.NET directory from AntiVirus. If you've tried those things and are still having an issue, or you just really want to see what is causing the problem, Process Monitor can help make sense of it all.

Let's use a simple example. I have an application which logs information to a file. Occasionally the logger dies that another process is using the file, and I'm rather confused why. I create the file, and as far as I know no one else is using it. My code looks like:

static void Main(string[] args)
{
  FileInfo info =
    new FileInfo(@"C:\logger\logfile.txt");
  try
  {
    using (StreamWriter writer = info.AppendText())
    {
      writer.WriteLine("Hello, World");
      writer.Flush();
      System.Threading.Thread.Sleep(10000);
    }
    Console.WriteLine("Finished write");
    System.Threading.Thread.Sleep(10000);

    using (StreamWriter writer = info.AppendText())
    {
      writer.WriteLine("Goodbye, World");
    }

  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.ToString());
  }
  Console.WriteLine("All Done");
}

As simple as this code is, I still see:

 

C:\logger>FileLockSpike.exe
Finished write
System.IO.IOException: The process cannot access the file 'C:\logger\logfile.txt
' because it is being used by another process.

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, I
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions o
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamWriter.CreateFile(String path, Boolean append)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encodin
g, Int32 bufferSize)
at System.IO.StreamWriter..ctor(String path, Boolean append)
at System.IO.FileInfo.AppendText()
at FileLockSpike.Program.Main(String[] args)
All Done

So let's track down why. First, I download and extract ProcessMonitor from the Microsoft Sysinternals site. It can be a little overwhelming at first - within 30 seconds of startup you see:

Yes - over 56,000 events. How will we ever find what we need? Luckily, we have a single file we can focus on - logfile.txt. So we can use Process Monitor's filters to discard everything but interesting events that happen to it. We do that by going into Filter -> Filter... and adding an exclusion filter for anything that isn't our path:

We should now see all the events go away, and a message that the current filter is excluding all events. So far, so good. So let's fire up our logger and see what happens:

With our application running, we now see all of the file access calls happening in the event log. We can see that it's all coming from our FileLockSpike.exe which looks good. Let's keep watching and see what happens. Aha! We got the error:

So let's see what Process Monitor says. That's strange. There are two different processes - FileLockSpike.exe, and FileLockSpike.vshost.exe. And that right after FileLockSpike.vshost.exe does a CreateFile, we get the exception in our app:

 

Let's double click on that vshost CreateFile line. This brings up the properties, and I can click over to the Process tab to see where it is coming from:

Interesting. It's coming from my \Documents\Visual Studio 2005\Projects\FileLockSpike\bin\Debug\ folder. And since the only thing I was doing at the time was debugging a new version of the logger application...

Well, that's a bit contrived. It's also what I get for hardcoding a path into my application. But Process Monitor is definately a valuable tool for tracking down what the heck is going on with your files and your system, and it is great fun just to watch what happens when you start up various things.

Happy monitoring!

Monday, October 15, 2007

Surviving in a Company

Whether you are in a small or large company, eventually you run into politics, and if you are really lucky, backstabbing, poor management and all sorts of other fun stuff. Some people get upset, and others start blogs. But when you get to the other side, you get to dispense with great advice like this:

 

For the purposes of your career, consider the whole company as just your manager, one level up, your closest peers, and your customers. This represents your "backyard" that you must keep clean for a healthy career. You'll give a lot to foster the relationships, but if it doesn't yield good things long term, move around in the company or even leave the company.

 

Don't get stuck in a rut thinking that this is like a sporting event where you can cry foul to a referee when you encounter unfairness. This manifests as running to skip level managers or HR with issues. Rarely are the skip level managers there to make things fair, rather they are there to be successful. HR is there for the company's benefit, not necessarily for you.

 

Your feet are your vote, and sometimes you have to lay low and take pain from bad managers until better opportunities show up, if you're willing to wait. This is much like my years in fast food as a teenager, where I had to make the investment to grow into better things. When you find a good manager with good peers, fight tooth and nail, put in late nights, etc. to stay there.

Wednesday, October 03, 2007

Adding a List of Links to a TFS Work Item

I had a question from a customer who needed to have a default list of links displayed on their Work Item in TFS. While one would think the LinksControl would be the ideal place, there isn't a good way to insert links to it without resorting to writing custom code. Fortunately there is an easier way - the HtmlControl.

To use it, open your work item type in the Process Editor and add a new field called "Default Links" or whatever you want to name it. The type should be HTML (I also chose not to make it reportable):

Now click the Rules tab and add a new DEFAULT rule. Choose "value" in the From drop-down, and paste the HTML that will make your list of links. Here I used:

<ul><li><a href="http://www.cornetdesign.com">Cory's Blog</a></li><li><a href="http://www.microsoft.com">Microsoft</a></li></ul> 

You also probably want to add a FROZEN or READONLY rule since it's unlikely you'll want this to change.

Once you've saved that field, click over to the Layout tab in the Work Item Editor. Find the place where you want the links to go and add a new control. Here I've created a new Group in the bug form for the links and am adding it there:

Pick the field we just added and label it whatever you'd like. Mark the field as ReadOnly, and then change the Type to HtmlFieldControl. This will render our HTML.

Save the work item type and reimport it to the server. If you have your team project open, right-click and refresh it, and then try creating a new work item of that type or opening an existing one:

Happy linking!