2010-09-30 54 views
4

我有一个WPF应用程序正在运行,它需要影响UI的所有操作都在UI线程上。 WPF还提供了一个Dispatcher类来处理这个 - 所以我将它提取到一个依赖项中。如何替换MEF容器中的导出零件/对象?

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 

回答

6

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

单元测试负责单独测试单个代码单元。 Mock被用来取代依赖关系。所以原则上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; 

(¹)从您的博客来看,我相信你已经知道这一点。但其他人也会阅读这个答案,所以我想澄清一下术语“单元测试”。

+0

这工作,但没有解决我的问题 - 现在'GetExportedValue '返回模拟。但是,创建一个导入IRole的对象仍然使用Real实现。例如'testContainer.GetExportedValue '仍然使用真正的实现。 – Gishu 2010-10-04 11:33:04

+0

并且与'单元测试'有关,是的,我用了错误的单词。我实际上正在编写一个NUnit的验收测试。 – Gishu 2010-10-04 11:34:11

+0

@Gishu:对不起,我从内存中写下了这个例子,而不是看我的测试,我有这个工作。我编辑了我的答案;该示例应该按照您现在的预期工作。诀窍是使用'CatalogExportProvider.SourceProvider'属性。 – 2010-10-04 16:44:24

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