Click here to Skip to main content
15,887,596 members
Articles / DevOps / Unit Testing
Tip/Trick

Unit testing imports in MEF - Loving that lazy interface

Rate me:
Please Sign up or sign in to vote.
4.88/5 (6 votes)
9 Dec 2013CPOL3 min read 25.1K   9   3
While we are all familiar with testing standard interface implementations, actually testing Lazy imports can be trickier if you don't follow this trick.

Introduction

Okay, it's well known that I love MEF with all it's composition-y goodies, and how I strive to write code to interfaces. As someone who believes that unit testing should be at the forefront of every developers mind, I spend a lot of time writing code that uses mocks of interfaces, but it seems to me that the process of actually testing MEFfed interfaces isn't immediately obvious to anyone who's new to the technology. 

Background 

While we can mock public imports, and invoking the composition can seem counter-intuitive, I have a preference to keep my import interfaces private.  If you're happy to expose your composition properties, then you could just mock and add your interfaces as necessary. If, like me, you want to hide the implementation details of your dependency injection so that someone doesn't accidentally do something to break your composition, this technique is for you. 

The setup

For this example, we're going to use three highly contrived interfaces; IAnimal, IDog and ICat.

C#
public interface IAnimal
{
  void MakeNoise();
}

public interface IDog : IAnimal
{
  void ChaseMailman();
}

public interface ICat : IAnimal
{
  void PlayWithYarn();
}

As I said, this is a highly contrived set of interfaces. Now, the code Iwewant to use here allows the application to retrieve either one of these three interfaces, using MEF to populate the application with a list of classes that implement these interfaces.

A bit of class   

For the purposes of this tip, I'm going to shortcircuit the whole TDD approach I like to work with, and show you the end product of this class: 

C#
using System;
using System.ComponentModel.Composition;

public interface IAnimalManager
{
  IEnumerable<Lazy<IAnimal>> _animals { get; set; }
}
[Export(typeof(IAnimalManager))]
public class AnimalManager : IAnimalManager
{
  [ImportMany]
  private IEnumerable<Lazy<IAnimal>> _animals { get; set; }

  public T GetAnimal<T>() where T : IAnimal
  {
    IAnimal animal = _animals.SingleOrDefault(pet => pet.Value is T);
    if (animal == null) return default(T);
    return (T)animal.Value;
  }
}

As we can see, this isn't a particularly complicated class, but it does rely heavily on MEF "filling in the blanks" so to speak. Specifically, it relies on things like MEF being able to compose a private Lazy property with all of the items it imports that matches the definition. 

Testing this bad boy

So, how exactly do we test this code? There are several things in here that could make the testing problematic. First of all, we have made the import property private - while we could, of course, have made the property public, this would have been a bad design decision because we are trying to keep our code self-contained. Also, we are using an enumerable lazy property - mocking this can be a bit of a pain. Also, we wouldn't be testing the behaviour of the system in the way we'd expect it to be used - this potentially defers a fair bit of pain into the integration testing phase. where it's a lot harder and more expensive to fix.

Fortunately, using MEF inside our test code is actually very, very simple. What we do is mimic the behaviour of MEF and do some composition "magic". Before we look at what the code is doing, let's actually see what our test looks like 

C#
[TestMethod]
public void AnimalManager_GetDog_RetrievesDog()
{
  // Mock the IDog interface for use in the composition part
  Mock<IDog> dog = new Mock<IDog>(); 

  // Create the concrete instance to compose into.
  AnimalManager animalManager = new AnimalManager();
  CompositionContainer container = new CompositionContainer();

  // Create an export composition of the mocked IDog interface
  container.ComposeExportedValue(dog.Object);

  // Actually compose the concrete animal manager instance.
  container.ComposeParts(animalManager);
  Assert.NotNull(container.GetAnimal<IDog>());
}

The first thing that we do is mock the IDog interface for use inside our import in the instance of the AnimalManager class that we have created. The clever bit is actually the next part; create an instance of the MEF CompositionContainer, and call ComposeExportedValue on our mocked IDog interface. Finally, we call ComposeParts passing in the concrete instance of the AnimalManager class. At this point, our class is now a properly composed class, and our test will run and behave itself. 

Final thoughts

I hope this has given you a flavour of how easy it can be testing a testable MEF application. Our MEFfed object isn't exposing it's properties to the world for testing purposes, so we can keep our compositions nice and tightly focussed. If we build an object that has multiple imports, we shouldn't worry about handling the interfaces we don't need to test a particular method - our tests should only fill in those parts of the composition container that we absolutely need for the test, so our tests shouldn't get too big.

Go forth, MEF and be fruitful. 

License

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


Written By
CEO
United Kingdom United Kingdom
A developer for over 30 years, I've been lucky enough to write articles and applications for Code Project as well as the Intel Ultimate Coder - Going Perceptual challenge. I live in the North East of England with 2 wonderful daughters and a wonderful wife.

I am not the Stig, but I do wish I had Lotus Tuned Suspension.

Comments and Discussions

 
Questionnice example Pin
Vick Blau17-Apr-14 5:53
Vick Blau17-Apr-14 5:53 
QuestionNice one. Short, but sweet! Pin
SteveTheThread9-Dec-13 21:54
SteveTheThread9-Dec-13 21:54 
Nice on Pete, short and to the point.
AnswerRe: Nice one. Short, but sweet! Pin
Pete O'Hanlon9-Dec-13 22:47
mvePete O'Hanlon9-Dec-13 22:47 

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

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