Showing posts with label MEF. Show all posts
Showing posts with label MEF. Show all posts

Sunday, November 7, 2010

Testing with Managed Extensibility Framework (MEF) for Silverlight

In the previous post, we developed test strategies for a three tiers, MVVM based Silverlight application, which uses the Managed Extensibility Framework (MEF) in order to loosen the coupling between the components and to provide better testability and extensibility.

In this post, we’ll see how the Managed Extensibility Framework (MEF) fit in with the overall testing strategy and try to figure out the best way to use it in the application in order to provide the best degree of testability.

To MEF or not to MEF (during tests)?

When coming to test MEF based application, the first question that comes to mind is whether to use the MEF composition container to instantiate dependencies during tests.

In component tests the answer is usually NO, in order to provide good degree of separation of concerns we need to isolate the component as much as possible, following that we should not let MEF interfere with the tests.

However, in some cases, the tested functionality uses MEF in order to provide special features such as downloading and loading xaps on the fly, in such cases we’ll need to put MEF into action and allow it to instantiate the dependencies. Since we’ll still need to replace the other components in the application with test doubles (fakes, mock, dummies, etc) – we’ll need to override the default composition container with a custom container that includes the real component under test and fake every thing else.

Don’t use the static composition container in the application

Since we need to exclude MEF in some (most) tests and to replace its composition container in others – we can’t use the default static composition container that MEF provides by default. Here’s why:

1) In order to replace the default static container we need to call CompositionHost.Initialize(container) that cannot be called more than once, which means that if more than one test will try to replace the container we’ll experience an ‘InvalidOperationException’ (this is a good example for the shared fixture anti-pattern) 

2) Even if we wouldn’t have to include MEF in any of our tests, when using the static container the CompositionInitializer.SatisfyImports (that put MEF into action) can be called from any class in the application, which means that we might not be not be able to exclude MEF in some of the tests 

Create composition container instance and call SatifyImportsOnce on the container itself

So what can we do? In order to retire ourselves from the static container, the application should create CompositionContainer object upon initialization, add the appropriate catalogs and/or the appropriate objects (via ComposePart) to the container, and call SatifyImportsOnce on the composition container itself in order to satisfy the imports of a given instance (usually the root object).

Allow replacing the container instance  with a test double

If internal classes in the application need to satisfy imports on the fly, we need to provide them with a way to access the global composition container such that we can replace the calls to the container with an empty implementation during tests.

In order to accomplish that, we’ll encapsulate the CompositionContainer with CompositionContainerService class that is accessible only though ICompositionService interface. We’ll introduce a new singleton called CompositionServiceProvide that will be shared widely and expose ICompositionService interface to enable access to the CompositionContainerService. The CompositionServiceProvide will allow replacing the CompositionContainerService with a dummy test double through the ICompositionServiceProviderTesting interface (implemented explicitly).

image

Here’s the code for the CompositionContainerService :

   1: public class CompositionContainerService : ICompositionService
   2: {
   3:     private readonly CompositionContainer m_container;
   4:     private readonly AggregateCatalog m_aggregateCatalog;
   5:  
   6:     public CompositionContainerService()
   7:     {
   8:         m_aggregateCatalog = new AggregateCatalog();
   9:         m_container = new CompositionContainer(m_aggregateCatalog);
  10:         
  11:     }
  12:  
  13:     public void SatisfyImports(object o)
  14:     {
  15:         m_container.SatisfyImportsOnce(o);
  16:     }
  17:  
  18:     public void ComposeParts(params object[] attributedParts)
  19:     {
  20:         m_container.ComposeParts(attributedParts);
  21:     }
  22:  
  23:     public void AddCatalog(ComposablePartCatalog catalog)
  24:     {
  25:         m_aggregateCatalog.Catalogs.Add(catalog);
  26:     }
  27:  
  28:     public CompositionContainer Container
  29:     {
  30:         get { return m_container; }
  31:     }
  32: }

Here’s the code for the singleton CompositionServiceProvider :

   1: public interface ICompositionServiceProviderTesting
   2: {
   3:     void SetCompositionService(ICompositionService compositionService);
   4: }
   5:  
   6: public class CompositionServiceProvider : ICompositionServiceProviderTesting
   7: {
   8:     static readonly CompositionServiceProvider s_instance = new CompositionServiceProvider();
   9:  
  10:     public static CompositionServiceProvider Instance
  11:     {
  12:         get { return s_instance; }
  13:     }
  14:  
  15:     private ICompositionService m_compositionService;
  16:  
  17:     public ICompositionService Service
  18:     {
  19:         get
  20:         {
  21:             if (m_compositionService == null)
  22:             {
  23:                 m_compositionService = new CompositionContainerService();
  24:             }
  25:  
  26:             return m_compositionService;
  27:         }
  28:     }
  29:  
  30:     void ICompositionServiceProviderTesting.SetCompositionService(ICompositionService compositionService)
  31:     {
  32:         m_compositionService = compositionService;
  33:     }
  34: }

 

Using MEF during tests

With MEF for Silverlight, developers can take advantage of the new DeploymentCatalog for downloading only a portion of the code when the user connect, and adding code (downloading extra xap files) on demand as the application makes progress. When a new xap is downloaded to the client browser and loaded into the container, MEF searches the xap for exports and start a process of re-satisfying the imports.

We’ll add the required functionality for downloading (AddXap) and loading (LoadXap) xaps to the container into the CompositionServiceProvider (described above)

   1: public class CompositionServiceProvider : ICompositionServiceProviderTesting
   2: {
   3:     ...
   4:  
   5:     readonly Dictionary<string, DeploymentCatalog> m_catalogs = new Dictionary<string, DeploymentCatalog>();
   6:  
   7:     public void AddXap(string uri, EventHandler<AsyncCompletedEventArgs> downloadCompletedCallback)
   8:     {
   9:         var catalog = new DeploymentCatalog(uri);
  10:         m_catalogs.Add(uri, catalog);
  11:  
  12:         catalog.DownloadCompleted += downloadCompletedCallback;
  13:         catalog.DownloadAsync();
  14:     }
  15:  
  16:     public void LoadXap(string uri)
  17:     {
  18:         DeploymentCatalog catalog = m_catalogs[uri];
  19:         Service.AddCatalog(catalog);
  20:  
  21:     }
  22:  
  23: }

Take the following application for example, when the user connect it’s presented with a basic UI that allows initiating basic activities, while the user is looking at the screen, another xap that includes an extra user control is downloaded in the background. When the user click on the ‘Extension’ button, the second xap (given that it is already available on the client) is loaded into the container, and as a result a dedicated property on the VIew that is configured to import the new user control (according to its special metadata) is automatically populated. Once the property is populated with the new user control, the View takes care of displaying the user control on the gray panel. 

Here’s the application:

image

Here’s the View code:

   1: [Export]
   2:  public partial class BooksPage : Page, IPartImportsSatisfiedNotification
   3:  {
   4:      [ImportingConstructor]
   5:      public  BooksPage(BooksViewModel viewModel)
   6:      {
   7:          InitializeComponent();
   8:  
   9:          DataContext = viewModel;
  10:      }
  11:      
  12:      [ImportMany(AllowRecomposition=true)]
  13:      public Lazy<UserControl, IWidgetMetadata>[] Widgets { get; set; }
  14:  
  15:      private void extentionButton_Click(object sender, System.Windows.RoutedEventArgs e)
  16:      {
  17:          // Load the xap (which as an effect will also re-satisfy the dependencies)
  18:          CompositionServiceProvider.Instance.LoadXap("PrototypeMVVM.Extentions.xap");
  19:      }
  20:  
  21:      public void OnImportsSatisfied()
  22:      {
  23:          wrapPanel1.Children.Clear();
  24:  
  25:          foreach (var widget in Widgets)
  26:          {
  27:              if (widget.Metadata.Location == WidgetLocation.Bottom)
  28:              {
  29:                  wrapPanel1.Children.Add(widget.Value);
  30:              }
  31:          }
  32:      }
  33:  }

In order to verify that once the ‘Extension’ button is clicked the appropriate xap is loaded and the View is updated appropriately -we need to take the following steps;

1)  Replace the original container with a new container

2)  Create a real View, real ViewModel, fake DAL and add them to the container via ComposePart (notice that we didn’t use static TypeCatalog in order to configure the container, It’s simpler to simply add the objects that are required to satisfy the imports)

3) Download the second xap and wait until the download complete.

4) Simulate click on the ‘Extension’ button

5) Verify that the new user control  is displayed

Here’s the test code:

   1: [TestMethod]
   2: [Asynchronous]
   3: public void CheckCompositionAfterPackageLoad()
   4: {
   5:     // Create the dependencies and add them to the container
   6:     var dalFake = new BooksDataServiceProviderFake();
   7:     var viewModel = new BooksViewModel(dalFake);
   8:     var view = new BooksPage(viewModel);
   9:  
  10:     // *** UsersWindow is a lazy import that is not being used in the test
  11:     // nevertheless, we need to instantiate it (or add it to TypeCatalog) and 
  12:     // its dependencies (and there dependencies) and add them to the container
  13:     var usersWindow = new UsersWindow();
  14:     var usersViewModel = new UsersViewModel(new UsersDataServiceProviderFake());
  15:  
  16:     var container = CompositionServiceProvider.Instance.Service.Container;
  17:     container.ComposeParts(view, usersWindow, usersViewModel);
  18:     container.SatisfyImportsOnce(view);
  19:  
  20:     TestPanel.Children.Add(view);
  21:  
  22:     // download the extension package
  23:     const string extentionsXap = "PrototypeMVVM.Extentions.xap";
  24:     CompositionServiceProvider.Instance.AddXap(extentionsXap, (sender, args) => m_xapDownloaded = true);
  25:  
  26:     EnqueueConditionalTimeoutChecker timeoutChecker = new EnqueueConditionalTimeoutChecker();
  27:  
  28:     // wait for download the complete
  29:     EnqueueConditional(() =>
  30:     {
  31:         timeoutChecker.Check();
  32:  
  33:         return m_xapDownloaded;
  34:     });
  35:  
  36:     // Click on the extention button
  37:     TestApi.EnqueueButtonClick(view.ExtensionButton, this);
  38:  
  39:     // since we only loading the xap and not downloading it, the UI re-composition should 
  40:     // happen synchronously
  41:     EnqueueCallback(() => Assert.AreEqual(1, view.WrapPanel1.Children.Count));
  42:     
  43:     EnqueueTestComplete();
  44: }

Sunday, October 24, 2010

Developing Silverlight 4.0 App with MVVM and MEF

In the previous post we walked through the process of developing three tiers web app using Silverlight 4.0, MVVM and WCF Data services. In this post, we’ll see how we can take advantage of the new Managed Extensibility Framework (MEF) for Silverlight in order to loosen the coupling between the parts in the application and by that 1) simplify the development process, 2) make our application more extendable and 3) much more and testable.

MEF is specially important for Silverlight since it provides a way to reduce the app startup footprint and dramatically improve the user's startup experience by using the DeploymentCatalog (previously PackageCatalog) feature, which allows breaking the xap file into pieces, download the basic xap on initialization, and download the other xaps a-synchronously on demand or immediately after startup.

In the next post, we’ll develop a testing strategy for the demonstrated application, which includes component level and multi-component level tests.

MEF Overview

MEF introduces a great way to integrate the dependency injection pattern into an application. It provides a standard way for a component to export any of its parts (classes) to other components, and to import external parts from any component in the xap boundaries.

In a typical application, when the host component starts, it instruct MEF to satisfy the imports tree of its root object, as a result, MEF make use of automatic exports discovery protocol in order to locate and instantiate the required objects and to wire them together in the correct order.

In order to export a class, we simply decorate it with the Export attribute.

   1: [Export]
   2: public partial class MainPage : UserControl
   3: {
   4: }

We can also export a class through its interface.

   1: [Export(typeof(IBooksDataServiceProvider))]
   2: public class BooksDataServiceProvider: DataServiceProvider , IBooksDataServiceProvider
   3: {
   4: }

In order to import an object of a given type into a property/field, we use the import attribute.

   1: public partial class App : Application
   2: {
   3:     [Import]
   4:     public MainPage MainView
   5:     {
   6:         get { return RootVisual as MainPage; }
   7:         set { RootVisual = value; }
   8:     }
   9: }

We can also use the ImportingConstructor attribute to specify that all the parameters of a given constructor should be treated as imports.

   1: public class BooksViewModel : ViewModelBase
   2: {
   3:     [ImportingConstructor]
   4:     public BooksViewModel(IBooksDataServiceProvider booksProvider)
   5:     {
   6:     }

In order to instruct MEF container to initiate the process of locating required exports and populating imports, we call CompositionInitializer.SatisfyImports(..)

   1: public partial class App : Application
   2: {   
   3:     private void Application_Startup(object sender, StartupEventArgs e)
   4:     {
   5:         CompositionInitializer.SatisfyImports(this);
   6:     }
   7: }

In the sample above, we instructed MEF to satisfy the imports of the App object, as a result, MEF searches all the assemblies in the xap in order to locate the classes that need to be imported and there dependencies. Which practically means that MEF search for MainPage, and if MainPage imports other classes in its implementation, MEF will import them as well.

Dependency Injection     

The dependency injection (DI) design pattern is used to promote ‘Separation of concerns’, Modularity, Extensibility and Testability. It dictates that all the parts in the application will communicate with there dependencies though interfaces, and that every part will be injected with all of its dependencies, rejecting the use of static objects and Singletons in the application.

With DI, there’s always some kind of a ‘container’ that make sure that the parts are injected with there dependencies. When implementing the DI pattern without the use of a dedicated framework, the application instantiate the parts and wire them up together.

With MEF, we only have to declare that part A depends on part B (the dependency), and a framework ‘dependency injection container’ take care of instantiating Part B and injecting it to part A. This is particularly useful when part B is shared among multiple parts scattered around the application, in such case, we don’t have to keep track of part B instance and figure out how to distribute it to its consumers (for instance, by using the Singleton pattern), the framework take care of it for us.

The Application

The new MEF based application looks exactly like the application reviewed in the previous post, but with the addition of a new window that displays list of users (pulled out from the data storage using a dedicated data provider).

image

High Level Design

In the previous post we used the DalSevice singleton class as a Dependency Lookup in order to allow replacing the data access layer with a Test Double when required.

Since we’ve chosen to inject dependencies instead of hard wiring the parts and using Singletons - we no longer need a bridge between the presentation parts (ViewModels) and the data provides, so we can retire ourselves from the singleton DalService,

With MEF, every ViewModel simply declare the interface of the data provide/s that it depends on, and the ‘dependency injection container’ make sure that it’s injected with them.

Here’s the new design:          

image

Now lets see some code!

Since we already reviewed the main components of the application in the previous post, we’ll focus on the code that was added to integrate MEF into the application.

The View Code Behind
   1: [Export]
   2: public partial class BooksPage : Page
   3: {
   4:     [ImportingConstructor]
   5:     public BooksPage(BooksViewModel viewModel)
   6:     {
   7:         InitializeComponent();
   8:  
   9:         DataContext = viewModel;
  10:     }
  11: }

The BooksPage is exported through MEF to its host (the main page), and injected with BooksViewModel on construction (notice the ImportingConstructor attribute).

The ViewModel
   1: [Export]
   2:  public class BooksViewModel : ViewModelBase
   3:  {
   4:      [ImportingConstructor]
   5:      public BooksViewModel(IBooksDataServiceProvider booksProvider)
   6:      {
   7:          ... 
   8:  
   9:          UsersCommand = new DelegateCommand<object>(o1 =>
  10:          {
  11:              var window = UsersWindow.Value;
  12:              window.Show();
  13:          });
  14:      }
  15:  
  16:      [Import]
  17:      public Lazy<UsersWindow> UsersWindow { get; set; }
  18:     
  19:      public ICommand UsersCommand { get; private set; }
  20: }
  21:  

The ViewModel is exported (for the favor of BooksPage) through MEF. It’s injected with the BooksDataServiceProvider on construction and with UsersWinodws on demand (notice the Lazy<T> paradigm)

The BooksDataProvider
   1: [Export(typeof(IBooksDataServiceProvider))]
   2:  public class BooksDataServiceProvider: DataServiceProvider , IBooksDataServiceProvider
   3:  {
   4:      private readonly BookStore m_bookStore;
   5:  
   6:      [ImportingConstructor]
   7:      public BooksDataServiceProvider(IDalConfiguration configuration)
   8:      {
   9:          m_bookStore = new BookStore(configuration.BooksDataServiceRoot);
  10:      }
  11: }

Since the data provider need to be replaced with alternative implementations (for testing in our case), we export it through its interface. In addition, we need to be able to change its configuration so we added a new configuration class that is injected to the data provider on construction.

The DalConfiguration
   1: [Export(typeof(IDalConfiguration))]
   2: public class DalConfiguration : IDalConfiguration
   3: {
   4:     public DalConfiguration()
   5:     {
   6:         BooksDataServiceRoot = new Uri("http://localhost:16081/BookStoreDataService.svc");
   7:         UsersDataServiceRoot = new Uri("http://localhost:16081/UsersDataService.svc");
   8:  
   9:     }
  10:  
  11:     public Uri BooksDataServiceRoot { get; set; }
  12:     public Uri UsersDataServiceRoot { get; set; }
  13:  
  14: }

The DalConfiguration class defines the URIs of the available providers. It’s exported through interface to provide support for changing the data services URI.

Unit Tests

During unit tests, in order to test the presentation logic in isolation we simply inject the ViewModel with a test double (usually fake) instead of the real data provider.

   1: var providerFake = new BooksDataServiceProviderFake();
   2:  
   3: var booksViewModel = new BooksViewModel(providerFake);

In order to test presentation logic against real data access layer, we can inject the ViewModel with a real data provider, and configure the data provider to target a fake/mock data service

   1: var dataServiceMockUri = 
   2:     new Uri("http://localhost:21978/BookStoreDataServiceMock.svc");
   3:           
   4: var configuration =
   5:     new DalConfigurationFake { BooksDataServiceRoot = dataServiceMockUri };
   6:  
   7: var provider = new BooksDataServiceProvider(configuration);
   8:  
   9: BooksViewModel viewModel = new BooksViewModel(provider);

Unit tests should not make use of MEF container for building dependencies. As presented above, a typical test starts with instantiating a ViewModel, instantiating its dependencies (here we can replace the dependencies with test doubles) and injecting the ViewModel with its dependencies. In integration tests, we run the entire application and let MEF container do its job, naturally, we’ll prefer to include the real parts rather than using test doubles.

However, in some cases we might want to use test doubles in integration test, to accomplish that we need to replace the default CompositionContainer, and use the CatalogExportProvider to allow ‘overriding dependencies’ and to instruct MEF to use our test doubles instead of product implementations.

   1: var productCatalog = new AssemblyCatalog(...);
   2:  
   3: // Wrap the product catalog with CatalogExportProvider to enable overriding dependencies
   4: var exportProvider = new CatalogExportProvider(productCatalog);
   5:  
   6: var testCatalog = new TypeCatalog(typeof(BooksDataServiceProviderMock));
   7:  
   8: // Notice, the test catalog is placed before the product catalog, this instructs MEF container 
   9: // to choose test exports on top of product exports
  10: CompositionContainer container = new CompositionContainer(testCatalog, exportProvider);
  11:  
  12: exportProvider.SourceProvider = container;
  13: CompositionHost.Initialize(container);

The source code for the application and the unit tests can be downloaded from here