Tuesday, January 04, 2011

Verifying PropertyChanged events in Silverlight using Fluent Assertions

Yesterday, I blogged about the newest release of Fluent Assertions for .NET and Silverlight and promised to demonstrate the new event monitoring syntax. Since I’m a Silverlight addict, I decided to show you an example of a MVVM unit test when using the traditional Assert class and the same example using Fluent Assertions.

Let’s start with the traditional example first. Notice the BDD-style I’m using here. If you want to learn more about it, check out my previous post on unit testing in Silverlight.

[TestClass]
public class When_activating : SpecificationContext
{
   
private FindActivitiesViewModel model
;
   
private IEnumerable<Product> products
;
   
private IEnumerable<Country> countries
;
   
private readonly List<string> changedProperties = new List<string
>();

   
protected override void EstablishContext
()
    {
       
model = new FindActivitiesViewModel
();

       
model.PropertyChanged += (sender, args) =>
changedProperties.Add(args.PropertyName
);

       
products = new
[]
        {
           
new ProductBuilder().Named("Trucks").Build
(),
           
new ProductBuilder().Named("Cars").Build
()
        };

       
countries = new
[]
        {
           
new CountryBuilder().Named("Netherlands").Build
(),
           
new CountryBuilder().Named("Belgium").Build
(),
           
new CountryBuilder().Named("Luxemburg").Build
()
        };
    }

   
protected override void Because
()
    {
       
model.OnActivate
();
    }

    [
TestMethod
]
   
public void It_should_expose_all_countries_sorted_by_name
()
    {
       
CollectionAssert.AreEqual(
countries.OrderBy(c => c.Name).ToArray(), model.Countries.ToArray());
       
CollectionAssert.Contains(changedProperties, "Countries"
);
    }

    [
TestMethod
]
   
public void It_should_expose_all_products_sorted_by_name
()
    {
       
CollectionAssert.AreEqual(
products.OrderBy(p => p.Name).ToArray(), model.Products.ToArray());
       
CollectionAssert.Contains(changedProperties, "Products"
);
    }

    [
TestMethod
]
   
public void It_should_not_expose_search_results_yet
()
    {
       
Assert.AreEqual(0, model.FoundActivities.Count
());
    }

    [
TestMethod
]
   
public void It_should_not_have_an_initial_selection
()
    {
       
Assert.IsNull(model.SelectedCountry
);
       
Assert.IsNull(model.SelectedProduct
);
    }

    [
TestMethod
]
   
public void It_should_not_be_possible_to_search
()
    {
       
Assert.IsFalse(model.CanSearch
);
    }
}

Pay some attention to the way I’m verifying that the property changed events were raised, and note the superfluous usage of ToArray() when comparing collections. Why on earth did MS decide to have CollectionAssert.AreEqual() take an ICollection instead of an IEnumerable?


Anyway, let’s see the equivalent example using Fluent Assertions.

[TestClass]
public class When_activating : SpecificationContext
{
   
private FindActivitiesViewModel model
;
   
private IEnumerable<Product> products
;
   
private IEnumerable<Country> countries
;
   
   
protected override void EstablishContext
()
    {
       
model = new FindActivitiesViewModel
();
       
model.MonitorEvents
();

       
products = new
[]
        {
           
new ProductBuilder().Named("Trucks").Build
(),
           
new ProductBuilder().Named("Cars").Build
()        };

       
countries = new
[]
        {
           
new CountryBuilder().Named("Netherlands").Build
(),
           
new CountryBuilder().Named("Belgium").Build
(),
           
new CountryBuilder().Named("Luxemburg").Build
()
        };
    }

   
protected override void Because
()
    {
       
model.OnActivate
();
    }

    [
TestMethod
]
   
public void It_should_expose_all_countries_sorted_by_name
()
    {
       
model.Countries.Should().Equal(countries.OrderBy(c => c.Name
));
       
model.ShouldRaisePropertyChangeFor(m => m.Countries
);
    }

    [
TestMethod
]
   
public void It_should_expose_all_products_sorted_by_name
()
    {
       
model.Products.Should().Equal(products.OrderBy(p => p.Name
));
       
model.ShouldRaisePropertyChangeFor(m => m.Products
);
    }

    [
TestMethod
]
   
public void It_should_not_expose_search_results_yet
()
    {
       
model.FoundActivities.Should().BeEmpty
();
    }

    [
TestMethod
]
   
public void It_should_not_have_an_initial_selection
()
    {
       
model.SelectedCountry.Should().BeNull
();
       
model.SelectedProduct.Should().BeNull
();
    }

    [
TestMethod
]
   
public void It_should_not_be_possible_to_search
()
    {
       
model.CanSearch.Should().BeFalse
();
    }
}
The difference may not be mind blowing. But if you care for intention revealing unit tests as much as I do, you should seriously consider checking out the documentation for the exception throwing and the property comparison assertion extensions.