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
.
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:
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
[TestMethod]
public void AnimalManager_GetDog_RetrievesDog()
{
Mock<IDog> dog = new Mock<IDog>();
AnimalManager animalManager = new AnimalManager();
CompositionContainer container = new CompositionContainer();
container.ComposeExportedValue(dog.Object);
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.