我运行了一个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
答案 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