如何替换MEF容器中的导出部件/对象?

时间:2010-09-30 06:28:02

标签: c# .net mef

我运行了一个WPF应用程序,它需要影响UI的所有操作都在UI线程上。 WPF还提供了一个处理此问题的Dispatcher类 - 所以我将其提取为依赖项。

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

因此,在我的生产代码中,我使用了一个委托给WPF Dispatcher的导出实现。我正在使用MEF进行DI。

现在问题是,在我的验收测试中,我需要替换容器中的部件/对象,该部件/对象通过模拟响应UIActionExecutor。因此,我需要从容器中删除ExecutorUsingWpfDispatcher并在其位置添加MockUIActionExecutor。这听起来很简单(如果我没有使用MEF)......但我的搜索技巧并没有帮助我找到关于如何使用MEF容器做到这一点的答案?

更新 如果有人想知道解决方案的原因/方式,请阅读Glenn Block's blog post#2。这就是我最终使用

    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

2 个答案:

答案 0 :(得分:7)

DI容器负责将所有内容连接在一起。

单元测试负责单独测试单个代码单元。模拟用于替换依赖项。所以原则上DI容器不应该用于单元测试。它与“单元测试”的定义相矛盾。(¹)

但是,我当然可以理解,除了单元测试之外,您可能还想进行自动集成测试,并且您可能希望在此类测试中使用MEF并替换某些MEF部件。你可以这样做:

// 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;

(¹)从你的博客来看,我相信你已经知道了。但其他人也会读到这个答案,所以我想清楚“单元测试”一词。

答案 1 :(得分:2)

无法获得已接受的解决方案。下面的代码应该起作用,优先级在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