I’ve heard some developers asking about UI testing lately. Specifically, there have been some questions about what we did with NHProf. So, I thought I would write a little bit about that here.
There are a variety of different types of tests you can do. In my experience some have a better ROI than others. For example, we don’t do any UI testing with automation APIs. Those are pretty painful, brittle tests to write and we haven’t worked in scenarios where they would really help us. The two main types of UI testing we tend to do are Unit Tests of the View Model and Binding Validation Tests against the Views.
View Model Tests
There are many advantages in taking a View Model approach to building your UI. When we build VMs we try and push as much of the UI state into our models as possible. By doing this we “virtualize” our entire UI, allowing us to simulate a large percentage of the UI behavior without ever opening a Window or showing anything on screen. This is great for testability. It means, we can use simple state based unit tests to verify the behavior of our UI. Here’s a simple example from NHProf:
public class StatementFilterTestFixture
{
private readonly FilterServiceModel service;
private readonly IList<IStatementSnapshot> statements;
public StatementFilterTestFixture()
{
statements = new List<IStatementSnapshot>
{
create_a_statement(100, "hello"),
create_a_statement(200, "select * from something"),
create_a_statement(300, "select field1 from something"),
create_a_statement(400, "select monkey,ninja,pirate,robot from floating_island"),
create_a_statement(500, ""),
};
service = new FilterServiceModel();
}
[Fact]
public void Can_filter_by_both_duration_for_values_less_than_300_and_contains_text()
{
var filters = new ObservableCollection<IFilter>
{
new FilterByDuration
{
Operator = ExpressionType.LessThan,
Value = "300"
},
new FilterBySqlContains {Text = "something"}
};
service.Filters = filters;
Assert.Equal(1, statements.Where(x => service.FilterStatement(x)).Count());
}
}
Binding Validation Tests
While the WPF/SL data binding mechanism opens the door to building rich View Models, it also introduces a new problem: potential binding expression errors. Because we didn’t want to give up the productivity benefits of databinding and View Models, we chose to take some time to build a binding validation framework. We then write simple tests to insure that there are no typos or silly mistakes in our binding expressions. Here’s an example test from NHProf:
public class StatisticsDetailsTestFixture : ViewTestFixtureBase
{
private readonly ValidationResult<StatisticsModel> bindings;
public StatisticsDetailsTestFixture()
{
bindings = Validator.For<StatisticDetailsView, StatisticsModel>()
.Validate();
}
[Fact]
public void BindingsDoNotHaveErrors()
{
Assert.False(bindings.HasErrors, bindings.ErrorSummary);
}
[Fact]
public void NameIsBound()
{
Assert.True(bindings.WasBoundTo(x => x.DisplayName));
}
[Fact]
public void StatisticsAreBound()
{
Assert.True(bindings.WasBoundTo(x => x.SortedStatistics));
}
}
You can actually take this one step farther as José Romaniello has in testing his Chinook Media Manager. Take a look at this sweet piece of code.
Posted
10-30-2009 3:07 PM
by
Rob Eisenberg