TDD: Using databind to object for the ultimate tdd exerience

Figured I need to share with you how I normally go about designing UI, and how I make that design as testable as possible.

To make things easy, I will use a login screen as an example, since it has few controls, and is relatively simple to follow.

Sample VS2008 solution can be downloaded here

The criteria

  • Create a login  dialog with a username, password, OK and Cancel button
  • OK Button is only enabled when both a username and password are set
  • Should be fully unit-testable

Implementation

I start by creating an empty solution with my typical folder structure.

image

The numbering is just something I add to the folders because I like to have the top-down view. Note that I have a clear separation between UserInterfaces and Presentation:

User Interface: Dumb dialog, form, or page containing bindable controls that the user interacts with

Presentation: Smart, data-bindable classes that represent a user interface’s logic and state.

 

Create a login  dialog with a username, password, OK and Cancel button

Next, I’ll add the windows forms project to the User Interfaces folder that contains my login dialog, and design our choice login box:

image

I am only interested in the design at this stage. Aside from setting the property UseSystemPasswordChar on the textbox for the password, and naturally giving the controls some meaningful names, I do not bother looking at code here.

 

Preparing to code

The next bit is half the magic. I am going to create a class to represent my login dialog. By implementing the INotifyPropertyChanged interface (found in System.ComponentModel), I am telling this clas that it can be databound to windows forms, WPF and silverlight controls.

I begin by adding a Presentation class library to contain the login class, as well as a test project where I can put all the facts related to it:

image

The Solution Items folder that you see at the bottom contains the test list and testrunconfig files, it is autogenerated by visual studio the first time you add a test project to your solution.

  • Presentation is a regular class library
  • Presentation.UnitTests is a test project

    Must be fully unit-testable

    In visual studio, it’s hard to write tests for objects that do not exist, this is due to intellisense trying to help you as you go actually turns into something you have to fight. Creating a skeleton Login class makes this process a lot easier.

    Initally, it looks like this:
public class Login : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler  PropertyChanged;

    public string Username            { get; set; }
    public string Password            { get; set; }
    public bool   OkButtonEnabled    { get; }
}

 

The PropertyChanged event is the mechanism used for databinding. More on that later.

Initially, I am interested in the following behavior from my login class:

image

Implementing these tests is fairly straight forward. When done, I can proceed to getting them to pas.

SIDENOTE: Unsure on how to verify events in a test? here is a smart way to do it:

[TestMethod]
public void PropertyChanged_SetPassword_EventFires( )
{
    // Prepare
    Login login            = new Login( );            
    bool eventWasFired    = false;
    login.PropertyChanged += (sender, e ) => eventWasFired = true;

    // Invoke
    login.Password = _testPassword;

    // Assert
    Assert.IsTrue( eventWasFired );
}

 

Back to our Login class, we want the Properties to “announce” that they have been changed,

this can be done like so:

public string Username            
{
    get{ return _userName; }
    set
    {
        if( _userName == value )
            return;
        //TODO: Validate username
        _userName = value;
        NotifyPropertyChanged( "Username" );
    }
}

private void NotifyPropertyChanged( string propertyName )
{
    if( PropertyChanged != null )
        PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}

Basically: If I can set the value, I announce it with my event handler. There is no point announcing a value that was never set.

Finally, the OkButtonEnabled property simply checks the username and password:

public bool OkButtonEnabled    
{
    get
    {
        if( string.IsNullOrEmpty( Username ) )
            return false;

        if( string.IsNullOrEmpty( Password ) )
            return false;

        return true;
    }
}

I’m a sucker for readability, can you tell? 🙂

After a very brief syntax check, I’m done with the Login class for now:

image

 

Binding it to the form

At this stage, I now have a Login form with absolutely no code behind it. Additionally, I’ve created a Login class that announces every property that changes, it is time to bind the two together. The process is simply:

  • Declare the login class as a data source
  • Bind properties from the login class to our form
  • Initialize the binding in the form’s constructor (in the code behind)
Declare the login class as a data source

In design mode, bring up the properties of the username textbox, find the databinding section. Since this is the first time we’re doing a data source, I can pre-select the Text property that I want to bind to, and then click Add project data source link

image

This brings up the following sequence (I’ll just run through the images, no comments should be necessary):

 

image

 

 

image

 

image

 

Having completed this process, you can now simply bind the TextBox.Text property to your bound class object with a simple drop-down:

image

 

The OK Button requires a special binding, because we want to bind it’s true/false value to the Enabled property, so we open up the Advanced data binding dialog:

image

Find the Enabled property, then simply choose to bind that to the OkButtonEnabled in our Login class:

image 

Press OK to save your changes.

Initialize the binding in the form’s constructor

The final step in the binding process is to perform some initalization on the login form, so that we have an actual object to store the values for username and password in. This can be passed to the form as a constructor argument, a property, or method, or be a built-in object. For the sake of this blog entry, I’ll simply use it as a publicly available property. Choose your login-form, switch to code view, and set the following lines of code:

public partial class frmLogin : Form
{
    // Our notification object:
    private Login _loginObject;

    public frmLogin( )
    {
        InitializeComponent( );

        _loginObject = new Login( );

        // Associate the databind with our notificationObject
        loginBindingSource.Add( _loginObject );
    }
}

 

That’s it.

When you run your application, you will see that the OK button does not enable itself before username and password have values. What you may find odd, is that you have to change focus from one textbox to another in order to see this. This is because the value from the textbox is only passed to the object when it loses focus. If you want a quicker, more live update, you can, for example, choose to update the object on the KeyUp event, as an example.

Summary

Databinding forms to class objects is simply a matter of implementing the INotifyPropertyChanged interface. You can only databind properties, but with a little fantasy, the possibilities are many.

You also have the added benefit of being able to unit-test ALL of the behavior that goes on in your dialog without requiring manual intervenion.

As a result, you can take presentation behavior classes with you from a windows forms project to a WPF or SilverLight project with very little effort, both tests and behavior are already coded, all you have to do is to bind that class to a different GUI. Rumor has it that Microsoft may do something to bring INotifyPropertyChanged funcionality to the asp.net platform aswell, but at the time of writing this blog entry, this is not supported.

Sample project can be downloaded here

5 thoughts on “TDD: Using databind to object for the ultimate tdd exerience

  1. lre says:

    Tears in my eyes 🙂

  2. René says:

    Hi there Pedro! Thanks very much for this thorough explanation.. there’s not many good tutorials on this subject! :-/
    So I was very happy to see your article, and I have tried stepping through your tutorial, only to find the control on the form remains unupdated!
    I have done everything as you said and it looks like it’s working too; by debugging I can see the value is changing, the NotifyPropertyChanged event fires, and everything.. yet no sign of all that on the form. Is there any way you could point me in the right direction? I was going to post my code here, but didn’t want to litter your article page with a “massive” piece of code. I’ve uploaded my example project here:
    http://www.f1rstracing.nl/temp/dev/DataBindingTest.rar (72kb)
    It’s made in VS Express 2010 for the .NET 2.0 Framework..
    Hope you can help me see where I’ve gone wrong!

    Many thanks in advance,

    René

  3. Pedro says:

    @René

    Hi René, looks like your update issue could be related to running the updates in a background worker thread and that the notification events does not come across from the background thread to the main thread where the GUI controls are.

    This is just a guess though, I have to get to a computer with Visual Studio to have a look at it.

  4. Pedro says:

    @René

    Bug found and squashed 🙂
    When running the counter in a background process, what you’ll want to do is to change the notification – because you’re updating Value in the background thread, the triggering of the updated value should happen through the backgound thread’s ProgressChanged notification mechanism:

    In essence:
    Main thread starts a background thread to change the values. The background thread tells the main thread “hey, I’ve updated the value” through the ProgressChanged event that backgroundWorkers have. This event is then tied to firing the NotifyPropertyChanged( “Value” ).
    public ValueUpdater(int frequency)
    {
    this.SleepTime = frequency;
    bgwUpdateValue.DoWork += bgwUpdateValue_DoWork;
    bgwUpdateValue.WorkerReportsProgress = true;
    bgwUpdateValue.ProgressChanged += (sender, e) => NotifyPropertyChanged("Value");
    }

    As you can tell from above, you’ll need to explicitly tell the background worker that it should open up for progress events.

    Inside your background worker function

    private void bgwUpdateValue_DoWork(object sender, DoWorkEventArgs e)
    {
    BackgroundWorker caller = (BackgroundWorker)sender;
    do
    {
    Thread.Sleep(this.SleepTime);
    Update_Value();

    // Just fire something to tell the main thread
    // that it should update
    caller.ReportProgress(1);
    }
    while (true);
    }

    You also need to remove the call to NotifyPropertyChanged on the Property Set code for value – it’s not a blocker, but it is sending Notifications to a thread that doesen’t respond to them (which is a code smell)

    Hoping that helps you 🙂

  5. René says:

    @Pedro
    Yes! It works ánd I understand it, thanks to your extensive explanation.. Thank you so much for your time and effort!

Leave a Reply