WPF/Silverlight MVVM Design Data - create once and share with Unit Tests

Getting data appearing in design view in Blend is key to being able to manipulate the your layout and styles without needing to run up your app (to quote Marcin, ‘nudge, nudge, nudge instead of nudge-F5-waaaait, nudge-F5-waaaaait’)

As soon as a colleague pointed out to me VS2010 now supports Blend’s d:DataContext I brought forward getting things ‘working in in design view’, as I realised here was a way to quickly and easily wire up a test rig of data in C# that I could share between my unit tests, a mock session of my WPF/Silverlight app and design view in both Blend and VS2010 (rather than xaml test data for UI, C# or for tests/runtime test UI). Down to it …

Working backwards, here’s my end result (Bob and Alice are a List<Person> bound to a ListView). Doesn’t look like much, but it works with all xaml controls including DataGrids/TreeViews etc, so you should quickly start to see the power in simplicity.

image

Key XAML:

<UserControl ... xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:md="clr-namespace:MvvmDemo" mc:Ignorable="d"
        d:DesignHeight="600" d:DesignWidth="800"
        d:DataContext="{x:Static md:DesignData.PersonListViewModel}"
        x:Name="view" DataContext="{Binding ElementName=view, Path=ViewModel}">
... <TextBox Text="{Binding Filter.PersonName}"
... <DatePicker SelectedDate="{Binding Filter.BirthDate}"
... <ListBox ItemsSource="{Binding FilteredPeople}">

By the way, this is very cool: the VS2010 Properties window now has navigation of my ViewModel object hierarchy, as long as I’m bound to my real DataContext in xaml. Still prefer the speed of resharper smart auto-completion though (ctrl+alt+space).

image

Anyway, back to the xaml … note the d:DataContext is bound to a static DesignData class & property. Here you could either inject a fake/mock repository and have the ViewModel do its thing, or stop it from loading and populate it in the DesignData class. Both are good techniques in unit testing and design data, and can be used interchangeably. Here I demo both (the viewmodel loads existing people from the repository, hence in design the fake, and I populate the form filter fields).

public static class DesignData
{
    static DesignData()
    {
        Container.Register<IPersonRepository, FakePersonRepository>();
    }

    public static PersonListViewModel PersonListViewModel
    {
        get
        {
            return new PersonListViewModel
                       {
                           Filter = new PersonFilter
                                        {
                                            PersonName = "Ca",
                                            BirthDate = DateTime.Now.AddYears(-20)
                                        }
                       };
        }
    }
}

I inject & reuse the FakeRepository in my TestUI exe (a separate exe so as not to pollute my release code with accidental fake data). 

And just for good measure, here’s a kind of contrived test – the fake data doesn’t really effect the test but you get the idea: write once, use everywhere - same as the rest of your code.

[TestMethod]
public void search_on_name_should_filter()
{
   // ARRANGE 
   _sut = new PersonListViewModel();
   var people = new FakePersonRepository().GetPeople();
   _sut.People.AddRange(people);
   people = new List<Person> { new Person {Name = "Carol"},
                        new Person {Name = "Carlos"},
                        new Person {Name = "Charlie"} };
   _sut.People.AddRange(people);
    string search = "Ca";
    _sut.PersonName = search;

   // ACT 
   _sut.Search();

   // ASSERT
   Assert.IsFalse(_sut.FilteredPeople.Any(p => !p.Name.StartsWith(search)));
   Assert.IsTrue(_sut.FilteredPeople.Any(p => p.Name.StartsWith(search)));
}

Full code here:

image

Technorati Tags: ,,
April 1 2010
blog comments powered by Disqus