Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Building a WPF Application: Part 1
Table of Contents

I actually meant to say in my last post that I had investigated the API options for FriendFeed and they have a C# wrapper already available here. It's packaged up in a single download with its Python and PHP counterparts.  Unfortunately, it has compilation errors. I fixed the errors and then I let ReSharper (R#) have its way with it [R#: Ctrl+Alt+Shift+F]. Yes, I do understand that the Code Cleanup feature of R# can be a nuclear bomb of churn for diffing your source, but egads I do love it so.

FriendFeed project in the solution explorerI also broke out the FriendFeed classes into separate files [R#: F6].

Hmm, I just decided that I'll notate R# and VisualStudio keyboard shortcuts like this [app:shortcut]. Is there already a convention for that out there?

I also decided to keep the FriendFeed code in a separate assembly. I don't always advocate assembly proliferation, but it made sense in this case. I created a project called FriendFeed and moved all their classes there.  I also preserved their namespace, FriendFeed, as well. (I'm getting tired of typing "FriendFeed".)

At this point, I just wanted to get a feel for the API. So I read over what appeared to be the significant classes, and I used a couple of tests to see how it really works. Simple enough, instantiate the client and call FetchHomeFeed().

Building in Seams

I picked up the term seam from Ayende in this post. You can think of a seam as a natural boundary in an application. It's a place where you easily extend or modify an application. It's a little bit like a Bounded Context, but just a little. I used to try to coerce my design into the metaphor of layers, but seams are a more natural concept. You might also think of them as a way of describing componentized architecture.

Consider the example of a desktop computer: you have a motherboard, CPU, memory, video card, etc. All of the components are required for the computer to function, but you can replace any one of them. You can extend the functionality of the computer by adding more hardware. Some things might be tightly coupled, like those wretched integrated video cards. But you don't think of these components as layers. This is a flexible way to architect your application.

Seams are a standard consideration for me when it comes to designing an application. Yes, you can go crazy with it, you can go crazy with anything. In the case of ChumChase, I don't want to be tightly coupled to the official API. I have a few reasons right now: [a] the official API might change. [b] I might want to implement my own API against their web services just for kicks [c] it's a good excuse to demonstrate the technique. I'm going to hide the FriendFeed API behind a repository. The IFeedRepository interface will be a seam in my application.

MVP

I mentioned before that I'm going to use Model-View-Presenter. This pattern is similar to MVC, and like barbeque sauce, everyone has their own flavor. In my case, a presenter is a class that represents the behavior of the UI, and possibly some state. It is ignorant of how the UI visually appears. The view will be a user control, window, page, tab item, etc. We'll use data binding to glue the two together. The ignorance of the presenter about the view is important because it helps us to isolate the presenter in our unit tests.

TDD

It's sometimes hard to decide where to start writing to tests. I tend to write tests that reflect the behaviors of the user interacting with the application. This is as opposed to testing how the application might interact with a data source, or some other more internal function of the application. This approach of working from the outside in helps prevent me from making too many assumptions about the internals.  The negative is that I have to provide mocks or stubs for the dependencies that I haven't written yet.

My core story, that is the basic behavior that I'm interested in, is for the application to fetch my home feed. I'll place this feature on a class called HomePresenter. HomePresenter might end up being my only presenter, I don't know yet. It common though for applications to have several presenters. We'll talk later about how to logically organize presenters.

Let's look at a test:

[TestFixture]
public class The_presenter_for_the_home_feed : TestFixtureBase
{
    private HomePresenter _presenter;
    private IFeedRepository _feedRepository;

    protected override void given_the_context_of()
    {
        _feedRepository = Mocks.StrictMock<IFeedRepository>();
        _presenter = new HomePresenter(_feedRepository);
    }

    [Test]
    public void can_refresh_the_home_feed()
    {
        using (Record)
        {
            Expect.Call(_feedRepository.FetchHomeFeed())
                .Return(new List<Entry>());
        }

        using (Playback)
        {
            _presenter.RefreshHomeFeed();
        }
    }
}

First, my TestFixtureBase is just an abstract class to reduce clutter. The Mocks, Record, and Playback properties all exist on it and they are really just pass-throughs to Rhino.Mocks. Likewise, I have a setup method in TestFixtureBase that calls given_the_context_of() before each test is executed. It's meant to reset the system-under-test (SUT) into a consistent and pristine state.

Notice that I named my class and method such that they read as a specification for the behavior I'm testing: The presenter for the home feed can refresh the home feed. I'm not sure that I like saying "presenter" in the specification. If I was working with non-technical users, that wouldn't make sense to them. I could replace it with "UI" or "interface" or "screen" perhaps.

The test has two blocks or phases: record and playback. This is generally a big stumbling block for people. I know when I first encountered it I was thoroughly confused. The record phase is where I tell the test what I'm expecting to happen during the playback phase.  The record phase plays the same roles as asserts.

The function we are really testing here is:

_presenter.RefreshHomeFeed();

Since there is not a state that I can verify after calling RefreshHomeFeed(), I am checking to make sure that it's doing what I expect it to do. That is, I expect it to get a list of entries from a repository of feeds.

The Expect.Call().Return() is Rhino.Mocks syntax that allows me to say: I expect my SUT to execute _feedRepository.FetchHomeFeed() and I expect that to return a List<Entry> instance. Right now Entry is just an empty class, but conceptually it represents a single item in the feed. You should also note that the relationship between my presenter and the repository is established in given_the_context_of().

Yes, this is a simple test, but writing this test helped me to shake out what I needed.  Unfortunately, the test as it stands here does not reflect the process of writing it.  I wrote some things, decided they didn't look right, changed them a bit and so on. It probably took me 10 minutes or so before I settled on what you see.

Moving on to the next test. This is in the same class, so you can read it as: The presenter for the home feed notifies the UI when the home feed is refreshed.

[Test]
public void notifies_the_UI_when_the_home_feed_is_refreshed()
{
    var property_has_changed = false;

    _presenter.PropertyChanged +=
        (s, e) => { property_has_changed = (e.PropertyName == "HomeFeed"); };

    _presenter.RefreshHomeFeed();

    Assert.That(property_has_changed);
}

I want to verify that when I call _presenter.RefreshHomeFeed() a change notification is raised for the HomeFeed property. This means my presenter needs a HomeFeed property. I have a boolean flag that helps me track the state and I'm using an inline delegate to set my flag when the property is changed. (There's actually a flaw in this test that we'll examine later, can you see it?) 

I don't like strings though. If I happen to change the name of the HomeFeed property and forget to change the string in the test, I've broken my test. Of course, I'll notice as soon as I run the tests, but it would sure be nice to eliminate the string. So I wrote this extension method for PropertyChangedEventArgs:

public static bool HasChanged<T>(this PropertyChangedEventArgs args, Expression<Func<T>> property)
{
    var expression = (MemberExpression)property.Body;
    return expression.Member.Name == args.PropertyName;
}

Now I can change my test to the following and eliminate the string:

[Test]
public void notifies_the_UI_when_the_home_feed_is_refreshed()
{
    var property_has_changed = false;

    _presenter.PropertyChanged +=
        (s, e) => { property_has_changed = e.HasChanged(() => _presenter.HomeFeed); };

    _presenter.RefreshHomeFeed();

    Assert.That(property_has_changed);
}

Of course, in between these tests I was writing code to make them pass. (Red -> Green -> Refactor, right?) Here's what my presenter class looked liked afterwards:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using ChumChase.Extensions;

namespace ChumChase.Presenters
{
    public class HomePresenter : INotifyPropertyChanged
    {
        private readonly IFeedRepository _feedRepository;
        private IList<Entry> _homeFeed;

        public HomePresenter(IFeedRepository feedRepository)
        {
            _feedRepository = feedRepository;
        }

        public IList<Entry> HomeFeed
        {
            get { return _homeFeed; }
            private set
            {
                _homeFeed = value;
                OnPropertyChanged( ()=> HomeFeed);
            }
        }

        public void RefreshHomeFeed()
        {
            HomeFeed = _feedRepository.FetchHomeFeed();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged<T>(Expression<Func<T>> property)
        {
            if (PropertyChanged != null) PropertyChanged(this, property.CreateChangeEventArgs());
        }
    }
}

The source is now available here. More to come!

Continue to Part 2.


Posted 07-22-2008 7:51 PM by Christopher Bennage

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)