Click here to Skip to main content
Click here to Skip to main content

Using MVC to Unit Test WPF Applications

, 27 Jan 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Provides guidance for using the Model-View-Controller design pattern to create modular WPF apps that are easy to unit test
spaghetti.jpg

Are you curious about how to create WPF applications that are modular, maintainable, and easy to unit test? Do you want some tips on how to avoid ending up in Spaghetti Hell during your next WPF project? Do you think that bowls make lousy hats? Read on…

Table of Contents

Introduction

At the time of this writing, the Windows Presentation Foundation (WPF) is a relatively new platform for creating Windows desktop applications. There is not too much available in the way of guidance for developers to follow when they need to “port” their existing application design skills and best practices to WPF. One critical, yet scarcely reviewed, topic is how to leverage the features and powers of WPF to create unit testable applications. This article begins the process of filling that void, by introducing techniques I developed and evolved while working on several real WPF applications over the course of two years.

Background

This article has been bouncing around in my head for a long time, just itching to get out. Initially I wanted to focus solely on how to implement the Model-View-Controller pattern in WPF, but eventually I realized that there is already some great material on the Web about that topic. This article introduces how to implement MVC with WPF, but that is merely a prerequisite for explaining how the application’s unit tests work. Toward the end of this article, I introduce the Model-View-ViewModel pattern that some very smart folks at Microsoft invented specifically for WPF. MVVM is a powerful and useful alternative to MVC when designing a WPF application.

Common Design Concerns

At the end of the day, we all want the same basic things out of our application designs, regardless of the UI platform. This section briefly reviews some of those common concerns, simply to provide context for the rest of this article. Nothing in this section is new or specific to WPF application design.

Any sensible software architect values modularity. Modular systems are made of self-contained units of functionality that have well defined relationships with each other. Changes made to one module should have a limited and predictable ripple effect on other, closely related, modules. An interface or base class often formally defines the relationship between two modules.

One great way to achieve modularity is by creating a layered architecture. This means that the various logical aspects of a system decompose into logical tiers, such as a data access layer, a domain/business layer, a presentation layer, etc. Each layer encapsulates certain concerns into a reusable and generalized abstraction, upon which the other tiers can depend. For example, the data access layer might be the only place in the system that actually reaches out and talks to a database. All other layers call into the data access layer, directly or indirectly, to perform data retrieval and persistence.

In a properly layered architecture, it is possible to achieve loose coupling between the user interface markup/code and the application logic that responds to user interaction. Having the application logic loosely coupled to the UI is beneficial in several ways. Replacing the UI at either compile time or runtime becomes easy to achieve, thus allowing visual designers to easily create and incorporate new views for the application. Maintaining and learning the application’s code becomes easier because the application logic is separate from the details of how the UI happens to be constructed.

Another great advantage to loosely coupling the application logic to the user interface is that it becomes simple to create unit tests that exercise application functionality without the entanglement of UI-specific concerns. This enables the development team to create a suite of unit tests that prove invaluable for automated regression testing, thus enabling the Quality Assurance team to focus on higher level testing than simply ensuring that the basic unit-level functionality still works properly.

Lower That Flamethrower

If you put ten software architects into a room and have them discuss what the Model-View-Controller pattern is, you will end up with twelve different opinions. In the next section of this article, I provide my definition of MVC. Some of the purists out there will inevitably have qualms with what I refer to as “MVC”. Feel free to leave a flaming comment on the message board at the bottom of this Web page. I will gladly entertain different perspectives on what MVC means, but keep in mind that I do not care. I am too busy getting work done to care about the “philosophically ideal” definition and implementation of MVC. If I must subscribe to some philosophy on this, I agree with Dr. WPF that the ideal pattern is M-V-poo.

Introducing MVC

The Model-View-Controller pattern is certainly nothing new. It has been around for decades. There have been many variations of it and many articles, book chapters, and blog posts written about it. The original impetus for the pattern was to help intelligently structure an application to handle low-level user input, back when UI controls were quite primitive. As the developer-friendliness and richness of UI controls has improved over time, MVC has taken on broader meaning.

These days MVC generally refers to the separation of concerns wherein you have a Model to represent the logic and data of the problem domain, a View to display the Model on-screen, and a Controller to handle user interaction events and inform the Model when processing needs to occur. The Controller is interested in user interaction; such as keyboard, mouse, or stylus input; for an entire form or user control…not just one button or dropdown.

Now that I have regurgitated a generic explanation of MVC, let us see what it really means in practice. Previously we saw how loosely coupling application logic to the UI has many desirable advantages. What we did not review was how to implement that loose coupling. As you have probably guessed by now, the MVC pattern is a fantastic way to do it.

The essence of a loosely coupled system boils down to one (bizarre) term that I coined: referencelessness. A loosely coupled system does not have unnecessary object references directly connecting its major functional components. Where there are direct references between parts of the system, they should often be expressed as interfaces or abstract base classes, to allow for dependency injection and object mocking (more on that later). The only places in the system where it makes sense to have a direct reference between two components is where the Controller references the Model, and possibly between the Controller and View in certain situations. As we examine how the demo application uses MVC, the reasoning behind these statements will become apparent.

Implementing MVC in WPF

This section reviews the essential features of WPF that we can use to promote referencelessness and modularity in our WPF applications. In subsequent sections, we put these tools to use and see a real example of how the pieces fit together to form a simple infrastructure on top of which real applications can thrive.

There are four fundamental pillars involved with implementing the MVC pattern in a WPF application. Here is a brief overview of each pillar, and what they contribute toward the overarching goals of referencelessness and modularity.

Routed Commands

The Command pattern is a well-established way to represent and execute actions in an application. A command represents an action that the application can perform. When various parts of the app need to perform that action, they ask the command to execute. Often the command object contains the execution logic, which encapsulates the entire operation into one convenient and reusable object.

Commands are a first-class feature in WPF. Implement the ICommand interface to turn an object into a command. However, WPF offers a much more powerful and sophisticated means of commanding than just that. By leveraging the event routing infrastructure baked into WPF, we also have the ability to create and consume “routed commands”. A routed command differs from a normal command in that it does not contain its own execution logic. Instead, it simply represents an action that the application might be able to perform, and delegates the actual execution logic to an external party.

When something in an application requests a routed command to execute, the command’s Executed routed event will bubble up the element tree. If some element in the tree established a handler for the Executed event of that command then the handler method will execute. Elements can also establish a handler for a command’s CanExecute routed event, allowing the application to determine if a command is able to execute at any given moment. WPF raises that event frequently for all routed commands, and disables any controls that are set to execute commands that cannot currently execute.

For example, suppose you have a WPF application used for editing documents. The application has a Save button on the toolbar, but if no document is open, WPF will automatically disable the Save button for you (assuming the button is wired to execute the Save command and the application’s Save command CanExecute logic returns false when no document is open). The application's Save menu item and Ctrl + S shortcut will also automatically be disabled, assuming they are wired to use the same Save command.

Elements in the element tree can establish event handlers for a routed command’s events by creating a CommandBinding object. If you add a CommandBinding to an element’s CommandBindings collection, you have essentially hooked the Executed and/or CanExecute events of some command.

Routed commands help us loosely couple the Controller to a View. Controls and elements in a View can execute routed commands for which the Controller has execution logic baked in. This means that the View does not reference its Controller, nor does the Controller care about what in the View executed the command. The Controller and View relate only via these “semantic identifiers”, not direct object references.

Learn more about commanding in WPF here.

Data Binding

Data binding is a way to automate the transfer of data between the Model and View. WPF provides a very comprehensive and flexible binding system, based entirely on runtime type information (a.k.a. .NET reflection). Since bindings are inherently late-bound, the View does not need a direct reference to the Model at compile time. This allows you to associate a View to the Model purely by the names of entities in the Model’s API, instead of to the class’s properties themselves. This layer of indirection provided by data binding allows the Model and View to remain loosely coupled. The Model can later be swapped out with a façade if necessary, and the View can easily be replaced with a new or culture-specific version either at runtime or compile time.

If your Model objects have their properties set by the application at runtime, they should implement the INotifyPropertyChanged interface. When a property on a Model object is set to a new value, that object should raise its PropertyChanged event to let the binding system know about the change. That will cause the UI to update and display the new value, assuming some element has a property bound to that property on the Model object.

Sometimes the data exposed by the Model needs to be transformed, formatted, or combined before being shown to the user. Conversely, sometimes the data entered by the user needs to be massaged before being sent back to the associated Model object. These types of data conversions are handled by creating a class that implements IValueConverter and assigning an instance of it to the Converter property of a Binding object. These objects are ‘value converters’, and are a very useful addition to any WPF developer’s toolkit.

You can validate the data passed between the View and Model in several ways. Which technique is appropriate to use is highly dependent on the specifics of the scenario and requirements. The worst option is to create ValidationRule subclasses and add them to a Binding’s ValidationRules collection. This is undesirable because you are creating platform-specific classes containing platform-neutral business logic.

In general, validation logic belongs in the domain/business layer. As of .NET 3.5, the WPF binding system works with the IDataErrorInfo interface. This interface has been in use for years and allows the validation logic to remain in the appropriate layer of the application. Either you can implement IDataErrorInfo on the Model, or, if you use the Model-View-ViewModel pattern (discussed later), implement it on the ViewModel. If your Model classes are strict about not allowing you to put them in an invalid state, or if you are using Model classes that you cannot change but to which you must add validation logic, then it makes sense to implement IDataErrorInfo on a ViewModel class instead of on the Model. However, I digress. We examine the MVVM pattern later.

Collection Views

When you bind to a collection in WPF, what you are really binding to is a view wrapped around the collection. WPF creates an ICollectionView behind the scenes and binds to it. A collection view allows you to sort, filter, and group the items in the underlying collection. It also allows you to programmatically discover and manipulate the “current” item, which translates into the selected item in the UI (e.g. the selected item in a ListBox).

In the WPF/MVC world we are creating here, the collection view is a crucial ingredient to help decouple the Controller from a View. In order for the Controller to not need a reference to a View, it can instead reference the collection view wrapped around a list of Model objects shown in the UI. If the View happens to display that list of Model objects in a ListBox, a Combobox, or a third party data grid, the Controller will never know or care. The Controller can listen to events on the collection view to know when the current/selected item has changed, and even call the MoveCurrentTo methods to set the selected item in the UI.

One shortcoming of this technique is that ICollectionView does not have any way of expressing a set of selected items; it only has the CurrentItem property. If a View allows the user to select multiple items in a list, the Controller needs to reference the View via an interface. That interface would have something like a CurrentItems property and a CurrentItemsChanged event. I hope to see this multiple selection functionality added to WPF’s ICollectionView in the future.

Resources

This might seem like a strange addition to our list of “fundamental pillars” for implementing WPF apps that are easy to unit test. You might be wondering how the resource system is relevant, since it typically just stores styles, templates, brushes, etc.

It turns out that you can use the WPF resource system as a simple and lightweight dependency injection Framework. You can store any type of object in a resource dictionary, and every element has a resource dictionary exposed by its Resources property. Programmatic discovery of resources is possible by calling either the FindResource or TryFindResource method on any element. We can use the resource system as a means of injecting mock objects into the Controller when running unit tests. A discussion about the practice of object mocking is beyond the scope of this article.

The Demo App

Before we dive into the demo application code (which is available to download at the top of this article), let us first see what the demo looks like. The application allows you to look at some x-ray images. The main screen consists of a simple list of x-rays and a button that, when clicked, opens a dialog displaying the image associated with the selected x-ray. This picture shows what the app looks like after clicking the ‘View X-Ray’ button when the selected x-ray is the first item in the list.

screenshot1.png

Notice in the next screenshot that once the image viewer dialog is closed, the x-ray that you viewed has its display text altered to indicate to the user that he has already viewed this x-ray image.

screenshot2.png

Please bear in mind that I have absolutely no experience with programming in the medical industry, no knowledge of x-rays, and am not suggesting that this is the “right” approach to take when designing a user interface for viewing x-rays. I just like to look at x-ray images of skulls!

How the Demo App Works

This section reviews how most of the demo application puts the features of WPF to use to implement MVC. Using the MVC pattern for this simple demo application is most certainly overkill, considering that the application is extremely simple and stupid. This is the perennial problem with creating demo apps: they need to be simple enough not to obfuscate the important information presented, but complicated enough to demonstrate the topic at hand. Consider this demo as simply a starting point from which the development of real applications can unfold.

Architecture Overview

The main window is XRayWindow. It hosts the XrayWindowView user control, which is the View that contains the controls seen in the UI. XrayWindow creates an instance of XrayWindowController in its constructor and delegates all decision making to that Controller. Also created in the XrayWindow’s constructor is an instance of XrayCollection, which contains a set of Xray objects. XrayCollection and Xray constitute the application’s Model. The XrayCollection is set as the DataContext of the window so that the View can bind to it. The Controller has a reference to the XrayCollection, so that it can get the collection view wrapped around it. Here is the XrayWindow constructor, which seems to be central to this application’s design:

public XrayWindow()
{ 
    InitializeComponent();

    XrayCollection xrays = XrayCollection.Load();

    // Create the controller that  
    // handles user interaction.
    _controller = new XrayWindowController(this, xrays);

    // Use the list of Xray objects as 
    // this Window's data source.
    base.DataContext = xrays;
}

Walking Through the Code

Now we will examine how the list of x-rays is displayed and what is in place to show an x-ray image when the user clicks the ‘View X-Ray’ button. Here is the XAML of the XrayWindowView (its code-behind is empty).

<UserControl 
  x:Class="TestableXrayDemo.Views.XrayWindowView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:demo="clr-namespace:TestableXrayDemo"
  xmlns:model="clr-namespace:TestableXrayDemo.Model"
  xmlns:views="clr-namespace:TestableXrayDemo.Views"
  >
  <UserControl.Resources>
  <!--<span class="code-comment"> 
  The resources were omitted for the sake of clarity.
  --></span>
  </UserControl.Resources>

  <!--<span class="code-comment">
  These are the controls seen in the main Window.
  --></span>
  <DockPanel Margin="2">
    <Button 
      Command="{x:Static demo:Commands.ShowSelectedXray}" 
      Content="View X-Ray"
      DockPanel.Dock="Bottom"
      HorizontalAlignment="Center" 
      Margin="0,4"
      />

    <ListBox 
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding Path=.}" 
      />
  </DockPanel>
</UserControl>

The UI consists of a ListBox and a Button control. The ListBox’s ItemsSource is bound to “.” meaning that it is bound to the inherited DataContext. Since the XrayWindow constructor set the window’s DataContext to an XrayCollection, and this View is parented to that window, the View inherits the XrayCollection as its DataContext. This means that the ListBox is binding to the collection view wrapped around an XrayCollection. Also, notice that the ListBox’s IsSynchronizedWithCurrentItem property is set to true. This ensures that it stays in sync with the current item of the collection view to which it is bound.

Now focus on the Button declared in the XAML above. Notice how its Command property is set to reference a static field of the Commands class called ShowSelectedXray. What you see here is an example of a View associating one of its controls with a routed command defined by the application. Here is the definition of the Commands class:

public static class Commands
{
    /// <span class="code-SummaryComment"><summary></span>
    /// Executed when the selected Xray's image should be displayed.
    /// <span class="code-SummaryComment"></summary></span>
    public static readonly RoutedUICommand ShowSelectedXray;

    static Commands()
    {
        ShowSelectedXray = new RoutedUICommand(
            Resources.ShowSelectedXrayCommandText,
            "ShowSelectedXray",
            typeof(Commands));
    }
}

At this point, we have seen how the View references a custom routed command and the command definition, but we still have not seen what happens when the command executes. That is a two-part process, starting in XrayWindow. Here is the XAML of XrayWindow:

<Window 
  x:Class="TestableXrayDemo.XrayWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:demo="clr-namespace:TestableXrayDemo"  
  xmlns:views="clr-namespace:TestableXrayDemo.Views"  
  FontSize="20"
  MaxWidth="800" MaxHeight="1000"
  MinWidth="250" MinHeight="200"
  SizeToContent="WidthAndHeight"
  Title="X-Ray Demo"
  WindowStartupLocation="CenterScreen"
  >

  <!--<span class="code-comment"> 
  Establish handlers for the ShowSelectedXray command's events. 
  --></span>
  <Window.CommandBindings>
    <CommandBinding 
      Command="{x:Static demo:Commands.ShowSelectedXray}" 
      CanExecute="ShowSelectedXray_CanExecute" 
      Executed="ShowSelectedXray_Executed" 
      />
  </Window.CommandBindings>

  <!--<span class="code-comment"> 
  This is the View applied to the Model.
  --></span>
  <views:XrayWindowView />

</Window>

The window has a CommandBinding in place that establishes handlers for the CanExecute and Executed routed events of our custom command. Those event handling methods are defined in the window’s code-behind, like so:

void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = _controller.CanShowSelectedXray;
}

void ShowSelectedXray_Executed(object sender, ExecutedRoutedEventArgs e)
{
    _controller.ShowSelectedXray();
}

As you can see above, XrayWindow simply delegates the command processing logic off to its Controller. This might seem like overkill for such a simple application, and it most certainly is. However, as discussed previously, as an application’s size and complexity increases it becomes increasingly beneficial to separate application interaction logic from some arbitrary UI container, such as a Window. Doing this also simplifies the creation of unit testing immensely since the tests do not have to worry about Window-specific issues and nuances.

Now let us examine how the Controller satisfies the user’s request to view the selected x-ray image. This code resides in XrayWindowController. First, we will inspect the property that determines if the user can view an image.

public bool CanShowSelectedXray
{
    // Return true if there is a selected Xray in the UI.
    get { return _xraysView.CurrentItem != null; }
}

The CanShowSelectedXray property determines if the application can currently display the selected x-ray image. It determines this by asking the collection view wrapped around the XrayCollection if there is a current item, which is akin to asking if the user has selected an x-ray in the ListBox yet. This is an example of how using the ICollectionView interface can decouple the Controller from a View. The Controller does not know anything about the ListBox in the XrayWindowView, nor should it.

If the user can view an image and clicks the ‘View X-Ray’ button, the following method is invoked.

public void ShowSelectedXray()
{
    #region Disclaimer

    // This method does not perform any null checks
    // for the sake of clarity and simplicity. In a 
    // real app a method like this should more robust.

    #endregion // Disclaimer

    // Get the Xray object selected in the UI.
    Xray selectedXray = _xraysView.CurrentItem as Xray;

    // Find the UI object for displaying x-ray images.
    // If running in a unit test we get a mock object.
    IXrayImageViewer xrayViewer =
        _xrayWindow.FindResource("VIEW_XrayImageViewer")
        as IXrayImageViewer;

    Uri imageLocation = GetXrayImageLocation(selectedXray);
    xrayViewer.ShowImage(imageLocation);

    // The UI detects the new value of HasBeenViewed
    // because Xray implements INotifyPropertyChanged.
    if (!selectedXray.HasBeenViewed)
        selectedXray.HasBeenViewed = true;
}

There are two points of interest in this method. The Controller makes use of the WPF resource system to get an object it can use to display the x-ray image. It does this by calling FindResource on the XrayWindow, passing in a well-known resource key, expecting to receive an object that implements the IXrayImageViewer interface. In the App.xaml file you will see that an instance of the XrayImageViewer window was added to the application’s resource dictionary, as seen below:

<Application 
  x:Class="TestableXrayDemo.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="clr-namespace:TestableXrayDemo.Views"
  StartupUri="XrayWindow.xaml"
  >
  <Application.Resources>
    <!--<span class="code-comment"> 
    This is the Window used to display x-ray images.  
    It is marked as not being shared because you cannot
    show a Window that has already been closed. Making  
    it unshared means a new instance is created every 
    time it is requested by the XrayWindowController.
    --></span>
    <views:XrayImageViewer 
      x:Key="VIEW_XrayImageViewer" 
      x:Shared="False" 
      />
  </Application.Resources>
</Application>

After the call to IXrayImageViewer.ShowImage returns, the Controller tells the selected Xray that it is now marked as “viewed”. It does this by setting the Xray object’s HasBeenViewed property to true. That property is defined like this:

public bool HasBeenViewed
{
    get { return _hasBeenViewed; }
    set 
    {
        if (value == _hasBeenViewed)
            return;

        _hasBeenViewed = value;

        this.OnPropertyChanged("HasBeenViewed");
    }
}

The XrayWindowView applies a Style to the items in its ListBox. That Style alters how the display text renders when the bound Xray object’s HasBeenViewed property returns true. The Style is notified of when an Xray has been viewed because the Xray object raises its PropertyChanged event in the setter. That Style is below:

<!--<span class="code-comment"> 
This alters an Xray's display text 
after its image has been viewed. 
--></span>
<Style x:Key="XrayTextBlockStyle" TargetType="TextBlock">
  <Style.Triggers>
    <DataTrigger 
      Binding{Binding Path=HasBeenViewed}"=" 
      Value="True"
      >
      <Setter Property="FontStyle" Value="Italic" />
      <Setter Property="Foreground" Value="Gray" />
    </DataTrigger>
  </Style.Triggers>
</Style>

Unit Testing the Model and Controller

At this point, we have seen how to create a WPF application that uses the MVC pattern. A result of this design practice is that we have a clean separation between the application’s data model, interaction logic, and user interface. Now that we have a loosely coupled system built of modular components, we can easily create unit tests that focus on the application’s interaction logic and data model. In a properly designed system, there should be no need to unit test the UI since it simply “consumes” the Controller. We want our unit tests and UI to both “consume” the Controller, so that testing the application logic programmatically is the same as testing it manually.

The demo application has a separate project that contains unit tests. I decided to use the unit testing Framework built into Visual Studio 2008 just for the sake of convenience. These tests would work fine in NUnit, or any other Framework you happen to use. They do not depend on any esoteric features of the built-in Visual Studio 2008 test Framework.

Testing the Model

The test for the Xray class is simple. It just makes sure that when the HasBeenViewed property is set, the object’s PropertyChanged event is raised properly. Here is that test method:

[TestMethod]
public void HasBeenViewedTest()
{
    DateTime creationDate = new DateTime();
    string fileName = string.Empty;
    XraySide side = XraySide.Front;
    Xray xray = new Xray(creationDate, fileName, side);

    Assert.IsFalse(xray.HasBeenViewed, "HasBeenViewed should return false now.");

    bool eventIsCorrect = false;
    xray.PropertyChanged +=
        delegate(object sender, PropertyChangedEventArgs e)
        {
            eventIsCorrect = e.PropertyName == "HasBeenViewed";
        };

    xray.HasBeenViewed = true;

    Assert.IsTrue(
      eventIsCorrect, 
      "Setting HasBeenViewed to true did not raise PropertyChanged event correctly.");

    Assert.IsTrue(xray.HasBeenViewed, "HasBeenViewed should return true now.");
} 

Simulating User Interaction

The more interesting unit tests are those that exercise the XrayWindowController class. First, let us examine the test method that verifies the output of the CanShowSelectedXray property:

[TestMethod]
public void CanShowSelectedXrayTest()
{
    XrayWindow xrayWindow = new XrayWindow();
    XrayCollection xrays = xrayWindow.DataContext as XrayCollection;                   
    XrayWindowController target = new XrayWindowController(xrayWindow, xrays);

    ICollectionView xraysView = CollectionViewSource.GetDefaultView(xrays);
    xraysView.MoveCurrentToPosition(-1);
    Assert.IsFalse(
      target.CanShowSelectedXray, 
      "Should not be able to show an image since no Xray is selected.");

    // Setting the position to zero is essentially the
    // same as selecting the first Xray in the ListBox.
    xraysView.MoveCurrentToPosition(0);
    Assert.IsTrue(
      target.CanShowSelectedXray, 
      "Should be able to show an image since an Xray is selected.");
} 

Now we can start to see the beauty of how implementing a decoupled system allows for rich unit testing capabilities. Since the Controller uses ICollectionView to find out what the selected Xray is in the View, we can programmatically simulate user interaction with a View by getting a reference to the same collection view and setting its current item. I am referring specifically to the calls to MoveCurrentToPosition, which instruct the collection view of the current item index. When the Controller asks the collection view what the current item is, it will return the Xray at whatever index we specify (or null if we specify an index of -1).

Injecting Mock Objects

The last unit test is slightly more involved. In this test, we want to verify that the Controller’s ShowSelectedXray method does two things: show an image and mark the Xray object as having been viewed.

This raises a slight problem. A unit test suite should be able to run to completion without requiring user interaction. This enables the tests to run on a continuous integration server, and prevents running them on your local machine from being an annoyance. If we were to test the Controller’s ShowSelectedXray method from a unit test, it would open the image viewer dialog and wait forever until someone came by and closed that dialog window. We could get hacky and put special logic into the XrayImageViewer code-behind that closes the window right away if running in a unit test. That is a terrible idea because it pollutes your application with code that is only relevant to a unit test. If you believe in the Broken Windows Theory, then this is obviously not the ideal solution. So what is the right thing to do?

As the title of this sub-section suggests, this is about a unit test that injects a mock object into the method under test. As seen previously, the ShowSelectedXray method uses the resource system to locate an instance of IXrayImageViewer. To refresh your memory, here is that method:

public void ShowSelectedXray()
{
    #region Disclaimer

    // This method does not perform any null checks
    // for the sake of clarity and simplicity. In a 
    // real app a method like this should more robust.

    #endregion // Disclaimer

    // Get the Xray object selected in the UI.
    Xray selectedXray = _xraysView.CurrentItem as Xray;

    // Find the UI object for displaying x-ray images.
    // If running in a unit test we get a mock object.
    IXrayImageViewer xrayViewer =
        _xrayWindow.FindResource("VIEW_XrayImageViewer")
        as IXrayImageViewer;

    Uri imageLocation = GetXrayImageLocation(selectedXray);
    xrayViewer.ShowImage(imageLocation);

    // The UI detects the new value of HasBeenViewed
    // because Xray implements INotifyPropertyChanged.
    if (!selectedXray.HasBeenViewed)
        selectedXray.HasBeenViewed = true;
}

When the application is running normally the call to FindResource returns the XrayImageViewer kept in the application’s main resource dictionary. However, we can create a dummy implementation of IXrayImageViewer and add it to the XrayWindow’s resource dictionary. Doing so will cause the method to use our mock object, which will not open a window or display anything at all. Here is the mock viewer class:

/// <span class="code-SummaryComment"><summary></span>
/// An implementation of IXrayImageViewer
/// for unit testing purposes.
/// <span class="code-SummaryComment"></summary></span>
private class MockXrayImageViewer : IXrayImageViewer
{
    public bool ShowImageWasCalled = false;

    public void ShowImage(Uri imageLocation)
    {
        this.ShowImageWasCalled = true;
    }
}

Here is the test method that creates the mock object and then tests the Controller with it:

[TestMethod]
public void ShowSelectedXrayTest()
{
    XrayWindow xrayWindow = new XrayWindow();

    // Add a fake image viewer to the window's resources
    // so that the controller being tested can use it.
    MockXrayImageViewer mockViewer = new MockXrayImageViewer();
    xrayWindow.Resources.Add(
        "VIEW_XrayImageViewer", 
        mockViewer);

    // Create the collection of x-rays and its default view.
    XrayCollection xrays = xrayWindow.DataContext as XrayCollection;
    ICollectionView xraysView = CollectionViewSource.GetDefaultView(xrays);

    // Select the first Xray object.
    Xray selectedXray = xrays[0];
    xraysView.MoveCurrentTo(selectedXray);

    // Perform the test.
    XrayWindowController target = new XrayWindowController(xrayWindow, xrays);
    target.ShowSelectedXray();

    Assert.IsTrue(mockViewer.ShowImageWasCalled, "ShowImage should have been invoked.");
    Assert.IsTrue(selectedXray.HasBeenViewed, "HasBeenViewed property should be true.");
}

When you run this test suite, you will see no windows popping open. Since the Controller calls ShowImage on our mock object, the test completes very quickly and displays nothing but happy little green success lights.

Introducing MVVM

Even though this article focuses on how to use my version of MVC for WPF, it is worth noting that I am not the first person to explore this territory. Some very smart people at Microsoft devised a variant of MVC tailor-made for WPF. They dubbed it the Model-View-ViewModel pattern, sometimes called the DataModel-View-ViewModel pattern.

In this pattern, the Controller morphs into the ViewModel. The ViewModel is essentially a fancy WPF-friendly wrapper around the underlying Model. The ViewModel exposes collections of data as observable collections, which enable rich data binding support. It also allows you to place domain validation and request processing in a layer that sits between the View and Model. When you use MVVM the View binds to the ViewModel, not the Model. In that sense, the ViewModel acts as an adapter between the “real” model and the user interface.

To learn a lot more about MVVM, check out these links:

Revision History

  • January 27th, 2008 – Created the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Josh Smith
Software Developer (Senior) Cynergy Systems
United States United States
Josh creates software, for iOS and Windows.
 
He works at Cynergy Systems as a Senior Experience Developer.
 
Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.
 
Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.
 
Check out his Advanced MVVM[^] book.
 
Visit his WPF blog[^] or stop by his iOS blog[^].
Follow on   Twitter

Comments and Discussions

 
AnswerRe: anti-pattern datamodel ? PinmvpJosh Smith23-Sep-08 4:04 
GeneralRe: anti-pattern datamodel ? Pinmemberelise.d25-Sep-08 1:53 
QuestionWhat's the best practice in WPF when handle multiple selected items? Pinmemberhanjack8-Sep-08 1:12 
AnswerRe: What's the best practice in WPF when handle multiple selected items? PinmvpJosh Smith8-Sep-08 2:20 
JokeThe Homer image was a nice touch! PinmemberTeufel121230-Jun-08 11:42 
GeneralRe: The Homer image was a nice touch! PinmvpJosh Smith30-Jun-08 11:43 
QuestionCan u please upload VS2005 version PinmemberSunil Soni8-Jun-08 20:12 
AnswerRe: Can u please upload VS2005 version PinmvpJosh Smith9-Jun-08 4:09 
QuestionWhat's the value in using routed commands? PinmemberD. Jin4-Jun-08 8:54 
AnswerRe: What's the value in using routed commands? PinmvpJosh Smith4-Jun-08 10:52 
AnswerRe: What's the value in using routed commands? Pinmemberzyp76829-Dec-10 3:52 
GeneralUnitTests project does not load in VS2008 Express PinmemberJohn Mairs2-Jun-08 23:50 
GeneralRegarding re-implementation PinmemberAndres Olivares8-May-08 17:07 
GeneralGreat Article PinmemberDerek Greer24-Mar-08 17:41 
GeneralRe: Great Article PinmvpJosh Smith25-Mar-08 3:42 
GeneralRe: Great Article PinmemberDerek Greer25-Mar-08 5:07 
GeneralRe: Great Article PinmvpJosh Smith25-Mar-08 5:20 
GeneralRe: Great Article PinmemberDerek Greer25-Mar-08 16:33 
GeneralOutstanding... and I have a question. Pinmemberjeristotle12-Mar-08 14:19 
GeneralRe: Outstanding... and I have a question. PinmvpJosh Smith12-Mar-08 14:36 
GeneralRe: Outstanding... and I have a question. Pinmemberjeristotle14-Mar-08 12:01 
GeneralGreat Article... But Question Pinmemberbyucygnus28-Feb-08 23:39 
GeneralRe: Great Article... But Question PinmvpJosh Smith29-Feb-08 3:10 
GeneralRe: Great Article... But Question Pinmemberbyucygnus29-Feb-08 4:08 
GeneralRe: Great Article... But Question PinmemberKenrae8-Sep-08 22:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141030.1 | Last Updated 27 Jan 2008
Article Copyright 2008 by Josh Smith
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid