Как заменить экспортируемую деталь / объект в контейнере MEF? - PullRequest
4 голосов
/ 30 сентября 2010

У меня работает приложение WPF, которому нужны все операции, которые влияют на пользовательский интерфейс, в потоке пользовательского интерфейса.WPF также предоставляет класс Dispatcher, который обрабатывает это - поэтому я извлек это в зависимость.

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

Так что в моем производственном коде я использую экспортированную реализацию, которая делегирует WPF Dispatcher.Я использую MEF для DI.

Теперь проблема в моих приемочных тестах, мне нужно заменить деталь / объект в контейнере, который отвечает на UIActionExecutor, на Mock.Поэтому мне нужно удалить ExecutorUsingWpfDispatcher из моего контейнера и добавить MockUIActionExecutor на его место.Это звучит довольно просто (если я не использовал MEF) ... но мои навыки поиска не помогли мне найти ответ на вопрос, как сделать это с контейнером MEF?

Обновление: Если кто-то хочет знать, почему и как работает решение, прочитайте Сообщение Глена Блока № 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 ]

7 голосов
/ 30 сентября 2010

Контейнер 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;

(¹) Судя по вашему блогу, я уверен, что вы уже знаете это.Но другие тоже прочтут этот ответ, поэтому я хотел прояснить термин «модульный тест».

2 голосов
/ 24 января 2013

Не удалось заставить принятое решение работать. Ниже код должен работать и приоритет описан в документации для 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
...