Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace an exported part / object in a MEF container?

Tags:

c#

.net

mef

I have a WPF app running, which needs all operations that affect the UI to be on the UI Thread. WPF also provides a Dispatcher class that handles this - so I extracted that into a dependency.

public interface UIActionExecutor
    {
        void Do(Action action);
    }

So in my production code, I use an exported implementation which delegates to the WPF Dispatcher. I'm using MEF for DI.

Now the problem, in my acceptance tests, I need to replace the part / object in the container that responds to UIActionExecutor by a Mock. So I need to remove ExecutorUsingWpfDispatcher from my container and add MockUIActionExecutor in its place. This sounds pretty simple (if I was not using MEF)... but my searching skills haven't helped me find an answer as to how to do this with the MEF container ?

Update: If anyone wants to know why/how the solution works - read Glenn Block's blog post#2. This is what I ended up using

    var defaultExportProvider = new CatalogExportProvider(__defaultCatalog);
    var catalogOfMocks = new AssemblyCatalog(assemblyExportingMocks);
    // order of params important (precedence left to right)
    __container = new CompositionContainer(catalogOfMocks, defaultExportProvider);
    defaultExportProvider.SourceProvider = __container
like image 865
Gishu Avatar asked Jan 21 '23 08:01

Gishu


2 Answers

A DI container is responsible for wiring everything together.

A unit test is responsible for testing a single unit of code in isolation. Mocks are used to replace dependencies. So in principle a DI container should not be used in a unit test. It contradicts the definition of "unit test".(¹)

However, I can certainly understand that you might want to do automated integration tests in addition to unit tests, and you might want to use MEF yet replace certain MEF parts in such a test. You can do that like this:

// first set up the main export provider
var mainCatalog = new AggregateCatalog(...); 
var mainExportProvider = new CatalogExportProvider(mainCatalog);

// now set up a container with mocks
var mockContainer = new CompositionContainer();
mockContainer.ComposeExportedValue<IFoo>(fooMock);
mockContainer.ComposeExportedValue<IBar>(barMock);

// use this testContainer, it will give precedence to mocks when available
var testContainer = new CompositionContainer(mockContainer, mainExportProvider);

// link back to the testContainer so mainExportProvider picks up the mocks
mainExportProvider.SourceProvider = testContainer;

(¹)Judging from your blog, I'm sure you already know this. But others will also read this answer, so I wanted to be clear about the term "unit test".

like image 132
Wim Coenen Avatar answered Mar 16 '23 00:03

Wim Coenen


Could not get the accepted solution to work. Below code should work and precedence is described in the documentation for AggregateExportProvider.

var mainContainer = new CompostionContainer();
mainContainer.ComposeExportedValue<IFoo>(new Foo() {Test = 1});

var mockContainer = new CompositionContainer();            
mockContainer.ComposeExportedValue<IFoo>(new Foo() {Test = 2});

var aggregateExportProvider = new AggregateExportProvider(
     mockContainer,   // IFoo in this container takes precedence
     mainContainer);

IFoo foo = aggregateExportProvider.GetExportedValue<IFoo>();

Console.WriteLine(foo.Test); // Outputs: 2
like image 38
CodeMonkey Avatar answered Mar 16 '23 02:03

CodeMonkey