Using Reactive Extensions (Rx) to Simplify Asynchronous Tests

Reactive Extensions (Rx) is a product from Microsoft Research that simplifies the management and composition of asynchronous events. If you read my earlier post on Asynchronous Workflows, you’ll understand why the asynchronous programming model can sometimes lead to confusing and hard-to-maintain code.

There is nothing wrong with the model, but the fact that you must subscribe to an event or register a callback means your code can end up nested and scattered to the winds. Add to that the complexity of aggregating events (for example, firing multiple asynchronous events and then waiting for them all to complete before going on) and it’s enough to require a visit to the pharmacy. OK, that was a bad lead-in because of course that’s where you can get your Rx (prescription).

There is a lot to explore in the framework, but you can start by visiting the main site and downloading the extensions (they are available for .NET 3.5, .NET 4.0, Silverlight 3, Silverlight 4 and JavaScript). Probably the best and easiest way to learn using Rx is to follow the hands-on labs that you can download in PDF format from this blog post.

I’d like to give a gentle introduction by demonstrating how it solved a very specific need of mine. A very common pattern is what I’ll call “synchronized lists” where one list triggers a change in the other. The classic example is selecting a country to get a list of states for that country. Selecting a country means we fetch or filter a new list of states, and if we’re kind to the end user we’ll also give them a default (either a state or perhaps a “select one” prompt). One test would be changing the country and validating that the list of states updated correctly. Another test, important for data-binding, is to ensure that setting the country raises both the country and the state property changed events. If someone were to come along and change the setter to the private property, the property change notification would fail to fire and we’d lose our data-binding.

To test is straightforward: set up the view model, register to the event, then listen and confirm we get the properties we are looking for. Implementing it is another story. The original syntax would involve registering a lambda that adds to a list, then waiting for a particular time (to make sure all events have fired) and then check the list. There is no “clean” way to do it without timers or a counter or making assumptions about the order the properties may come in.

The Reactive Extensions simplify this tremendously. Take a look at the following code:

public void TestChangeCurrentCountry_RaisesPropertyChanged()
{
    var properties = new List<string>();
    var expected = new[] {"CurrentCountry", "CurrentState"};

    // set it up
    _target.OnImportsSatisfied();

    var propertyChanged = (from evt 
      in Observable.FromEvent<PropertyChangedEventArgs>(_target, "PropertyChanged")
      select evt.EventArgs.PropertyName).Take(2);
                
    using (propertyChanged.Subscribe(
      properties.Add,
      ex => Assert.Fail(ex.Message),
      () =>
         {                                                 
            CollectionAssert.AreEquivalent(expected, properties,
            "Invalid properties were raised on the notify change event.");
            EnqueueTestComplete();
         }))
    {
        _target.CurrentCountry = _countries[1];
    }
}

What’s going on here? I set up a list to grab properties as they are raised, and I initialize a list of expected properties. This makes the conditions of the test very clear. I’m using MEF, so what happens in unit tests is that I set up the view model with a bunch of mock objects to return my data: i.e. “if you ask for a country, I’ll give you this” and so forth. I explicitly call “OnImportsSatisfied” to simulate what would happen if I were actually composing the view model with MEF (in this case, however, my test is composing it).

In this case, the view model will call the country service to populate the list, then the state service, and set defaults. All of these are fed based on the mock configurations.

Once we have the view model prepared, the Rx steps in. I set up an observable collection that is based on the property changed event. Notice we pass the expected EventArgs, the object to inspect and the event to listen for. The power of Rx is that it will asynchronously build the list for me as the event is raised, in this case returning the property names (because of my select statement). I am only expecting 2, so I can use the standard LINQ “Take(2)” for the list to stop after the second property changed event (otherwise, it would continue listening and building out the list ad infinitum).

The using statement provides an action as items are added (note I’m using a method group to specify that the string is passed to the add method of the properties list.) If an exception is thrown, I’ll fail the test gracefully with an assert. The last parameter is the action to perform when complete, which is after both properties have been added. Here, I compare the two lists and let the Silverlight Unit Testing Framework know that the test is complete.

The entire statement wraps the functionality I’ll use to trigger the behaviors – in this case, changing the country to set off the property change notification.

While this is a very simple example, I encourage you to download the hands on labs to see more complex examples that compose multiple asynchronous events together in a single collection. It’s a powerful framework that deserves a close look!

Jeremy Likness

Run your applications in the fastest growing public cloud.

Getting started can be a challenge and many of our customers tried it on their own and had mixed results. We offer trusted, transparent, and secure solutions for business applications deployed in both Infrastructure as a Service (IaaS) or Platform as a Service (PaaS) public Azure deployments.

Accelerate your Azure adoption.

We partner with your team to identify what belongs in Azure and how best to leverage its features.

Break monolithic architectures.

We help you take advantage of Microservices to reduce complexity and increase scalability.

Optimize your investment.

Together we will plan and control how much is consumed and ensure your cloud is secure and meets compliance mandates.

Why Azure and Atmosera?

Azure is a public cloud with true global reach.

Azure has more data centers than AWS and Google Cloud combined. It offers an unparalleled global reach with 32 regions in various deployment stages. Microsoft has invested in excess of US$15 billion and continues to drive this strategic investment. Azure offers our customers a secure, scalable, and competitive environment to deploy applications anywhere in the world.

Atmosera tackles the ongoing challenges of running in Azure.

We were one of the first Microsoft Cloud Solution Providers (CSPs), we are Cloud OS Network (COSN) certified with many production environments on the Cloud Platform System including Azure Certified for Hybrid deployments leveraging private and public clouds. We know how to make Azure work for you.

Atmosera common services

  • Personalized fully managed services and operational continuity
  • Enterprise-grade uptime and availability
  • Integrated monitoring and proactive support from a team of experts
  • Technical Assistance Command Center (TACC) available live 24x7x365
  • Flexible safeguards to meet various compliance requirements including HIPAA/HITECH, HITRUST, IRS-1075 and PCI-DSS
Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X