2015-09-06 2 views
3

Приложение WPF, над которым я работаю, будет иметь множество надстроек для расширения его функциональности. Каждая надстройка будет состоять из одной или нескольких сборок (обычно это «основная» надстройка с отдельными сборками для компонентов и представлений уровня представления), но будет обрабатываться приложением как единый модуль Prism. Призма будет использоваться для обнаружения и загрузки надстроек с использованием MEF, чтобы гарантировать, что модуль Prism сможет получать доступ к зависимостям в связанных сборках надстройки. Метод Initialize модуля Prism будет, помимо прочего, отвечать за настройку контейнера IoC (в данном случае Unity). В развернутом сценарии все это будет загружаться и управляться с помощью MefBootstrapper при запуске.Модуль Load Prism с автономным менеджером модулей

Проблема возникает при попытке модульного тестирования надстройки. Чтобы код надстройки был полуизолирован из основного приложения, каждая надстройка также будет иметь свои собственные тестовые сборки модулей. Один из этих тестовых сборок будет отвечать за проверку регистрации услуг с контейнером IoC. В тестовом сценарии я не хочу использовать загрузчик для загрузки модулей Prism, поскольку у них есть зависимости, которые я не хочу вводить в свои тестовые сборки. Поэтому я написал свой базовый класс test fixture, чтобы он создал свой собственный MefModuleManager для загрузки тестируемого модуля.

ResolutionTestBase.cs

public abstract class ResolutionTestBase 
{ 
    [ClassInitialize] 
    public static void TestFixtureInitialise(TestContext context) 
    { 
    // Create the main resolution container. 
    var container = new UnityContainer(); 

    // Install the service locator. 
    var locator = new UnityServiceLocator(container); 
    ServiceLocator.SetLocatorProvider(() => locator); 
    } 

    // Here go some helper methods for performing resolution tests. 

    protected IUnityContainer Container 
    { 
    get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; } 
    } 
} 

AddInResolutionTestBase.cs

public abstract class AddInResolutionTestBase:ResolutionTestBase 
{ 
    static AddInResolutionTestBase() 
    { 
    Logger = new EmptyLogger(); 
    } 

    [TestInitialize] 
    public virtual void TestInitialise() 
    { 
    // Create MEF catalog. 
    var aggregateCatalog = new AggregateCatalog(); 
    foreach (var testAssembly in TestAssemblies) 
    { 
     aggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly)); 
    } 

    // Load module manager. 
    var container = new CompositionContainer(aggregateCatalog); 
    var serviceLocator = new MefServiceLocatorAdapter(container); 
    var parts = new DownloadedPartCatalogCollection(); 
    var moduleInitialiser = new MefModuleInitializer(serviceLocator, Logger, parts, aggregateCatalog); 
    var moduleManager = new MefModuleManager(moduleInitialiser, ModuleCatalog, Logger); 
    moduleManager.ModuleTypeLoaders = new[] { new MefFileModuleTypeLoader() }; 
    moduleManager.Run(); 
    } 

    protected static ILoggerFacade Logger { get; private set; } 
    protected abstract IModuleCatalog ModuleCatalog { get; } 
    protected abstract IEnumerable<Assembly> TestAssemblies { get; } 
} 

Для надстроек, они имеют автономный класс в их основной сборки для выполнения требований модуля Prism, и Unity для настройки контейнера.

Module.cs

[ModuleExport("AddInModule", typeof(Module), InitializationMode = InitializationMode.OnDemand)] 
public class Module : IModule 
{ 
    private readonly IEnumerable<IUnityContainerExtensionConfigurator> _extensions; 

    [ImportingConstructor] 
    public Module([ImportMany]IEnumerable<IUnityContainerExtensionConfigurator> extensions) 
    { 
    _extensions = extensions; 
    } 

    public void Initialize() 
    { 
    // Load the dependency injection container. 
    var container = ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; 
    if (container != null) 
    { 
     foreach (var extension in _extensions) 
     { 
     container.AddExtension((UnityContainerExtension) extension); 
     } 
    } 
    } 
} 

ContainerInstallerExtension.cs (в основном блоке Надстройка в)

[Export(typeof(IUnityContainerExtensionConfigurator))] 
public class ContainerInstallerExtension : UnityContainerExtension 
{ 
    protected override void Initialize() 
    { 
    // perform container configuration here. 
    } 
} 

PresentationInstallerExtension.cs (в сборке презентации Надстройка в)

[Export(typeof(IUnityContainerExtensionConfigurator))] 
public class PresentationInstallerExtension:UnityContainerExtension 
{ 
    protected override void Initialize() 
    { 
    // perform container configuration here. 
    } 
} 

AddInResolutionTest.cs (в сборке для тестирования IoC)

[TestClass] 
public class AddInResolutionTest : AddInResolutionTestBase 
{ 
    private IEnumerable<Assembly> _testAssemblies; 

    private IModuleCatalog DoGetModuleCatalog() 
    { 
    var moduleInfo = new ModuleInfo("AddInModule", typeof (Module).AssemblyQualifiedName) 
    { 
     InitializationMode = InitializationMode.WhenAvailable, 
     Ref = typeof (Module).Assembly.CodeBase 
    }; 
    return new ModuleCatalog(new[] {moduleInfo}); 
    } 

    protected override IModuleCatalog ModuleCatalog 
    { 
    get { return DoGetModuleCatalog(); } 
    } 

    protected override IEnumerable<Assembly> TestAssemblies 
    { 
    get { return _testAssemblies ?? (_testAssemblies = new[] { typeof(ContainerInstallerExtension).Assembly, typeof(PresentationInstallerExtension).Assembly }); } 
    } 

    [TestMethod] 
    public void ResolveSomeService() 
    { 
    // perform resolution test here. 
    } 
} 

отметить испытуемым разрешение прибора, то «пробные сборки» связаны с тестовой сборки IoC со ссылками проекта и называют непосредственно по типу (а не с помощью каталога сканирования каталога), так что я мог бы избежать необходимо использовать событие post-build для копирования сборок в общую папку для тестирования.

При запуске модульных тестов (как есть), я получаю исключение, указывающее менеджер модуль не удалось загрузить модуль Prism:

Способ инициализации AddInResolutionTest.TestInitialise выбросил исключение. Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Не удалось загрузить тип для модуля AddInModule.

Если эта ошибка возникла при использовании MEF в приложении Silverlight, убедитесь, что свойство CopyLocal ссылки на сборку MefExtensions установлено в true в главном приложении/оболочке и false во всех других сборках.

Ошибка была: ссылка объекта не установлена ​​в экземпляр объекта .. ---> System.NullReferenceException: ссылка на объект не установлена ​​в экземпляр объекта. в Microsoft.Practices.Prism.MefExtensions.Modularity .MefFileModuleTypeLoader.LoadModuleType (ModuleInfo ModuleInfo) --- Конец внутренней трассировки стека исключений --- в Microsoft.Practices.Prism.Modularity.ModuleManager.HandleModuleTypeLoadingError (ModuleInfo ModuleInfo, исключение) исключение в Microsoft.Practices.Prism.Modularity .ModuleManager.IModuleTypeLoader_LoadModuleCompleted (отправитель объекта, LoadModuleCompletedEventArgs e) в Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted (LoadModuleCompletedEve ntArgs е) на Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted (ModuleInfo ModuleInfo, ошибка Исключение) в Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType (ModuleInfo ModuleInfo) на Microsoft.Practices. Prism.Modularity.ModuleManager.BeginRetrievingModule (ModuleInfo ModuleInfo) на Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModuleTypes (IEnumerable`1 moduleInfos) на Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable() в Microsoft.Practices .Prism.Modularity.ModuleManager.Run() в AddInResolutionTestBase.TestInitialise() в AddInResolutionTestBase.cs: строка xx

В момент звонка moduleManager.Run() ничего в моем коде не имеет значения, так что мне непонятно, что такое «настоящая» проблема.

Я пробовал различные изменения, чтобы устранить эту проблему в то числе:

  • призывающей moduleManager.LoadModule() вместо moduleManager.Run() в AddInResolutionTestBase.cs
  • манипулирования State из ModuleInfo созданных в AddInResolutionTest, чтобы обойти эту проблему в менеджере модулей

Любые другие изменения, которые я сделал, привели к различным ошибкам, но все же указывают на проблему с модулем m который пытается загрузить модуль Prism.

Есть ли еще один шаг, необходимый для правильной настройки диспетчера модулей, чтобы иметь возможность загружать модули таким образом, учитывая, что некоторые обычные служебные данные (например, регистратор) не требуются для модульных тестов?

ответ

1

С помощью декомпилятора я смог выяснить «недостающие части» и внести некоторые изменения, чтобы гарантировать, что все необходимые компоненты зарегистрированы/установлены для диспетчера модулей, чтобы иметь возможность инициализировать модуль (ы) для тестирования. Для тех, кто заинтересован:

public abstract class AddInResolutionTestBase:ResolutionTestBase 
{ 
    private CompositionContainer _container; 
    private IModuleCatalog _moduleCatalog; 
    private IEnumerable<object> _testEntities; 
    private IEnumerable<ModuleInfo> _testModuleInformation; 

    static AddInResolutionTestBase() 
    { 
    Logger = new EmptyLogger(); 
    } 

    [TestInitialize] 
    public virtual void TestInitialise() 
    { 
    // Create MEF catalog. 
    AggregateCatalog = CreateAggregateCatalog(); 
    ConfigureAggregateCatalog(); 
    AggregateCatalog = DefaultPrismServiceRegistrar.RegisterRequiredPrismServicesIfMissing(AggregateCatalog); 

    ConfigureContainer(); 

    // Initialise modules to be tested. 
    CompositionContainer.GetExportedValue<IModuleManager>().Run(); 
    } 

    #region Protected Methods 
    protected virtual void ConfigureAggregateCatalog() 
    { 
    var testAssemblies = TestEntities.OfType<Assembly>(); 
    foreach (var testAssembly in testAssemblies) 
    { 
     AggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly)); 
    } 

    if (TestEntities.Any(entity => entity is System.Type)) 
    { 
     var catalog = new TypeCatalog(TestEntities.OfType<System.Type>()); 
     AggregateCatalog.Catalogs.Add(catalog); 
    } 
    } 

    protected virtual void ConfigureContainer() 
    { 
    CompositionContainer.ComposeExportedValue<ILoggerFacade>(Logger); 
    CompositionContainer.ComposeExportedValue<IModuleCatalog>(ModuleCatalog); 
    CompositionContainer.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(CompositionContainer)); 
    CompositionContainer.ComposeExportedValue<AggregateCatalog>(AggregateCatalog); 
    } 

    protected virtual AggregateCatalog CreateAggregateCatalog() 
    { 
    return new AggregateCatalog(); 
    } 

    protected virtual CompositionContainer CreateContainer() 
    { 
    return new CompositionContainer(AggregateCatalog); 
    } 

    protected virtual IModuleCatalog CreateModuleCatalog() 
    { 
    return new ModuleCatalog(TestModuleInformation); 
    } 

    protected abstract IEnumerable<object> GetTestEntities(); 
    protected abstract IEnumerable<ModuleInfo> GetTestModuleInformation(); 
    #endregion 

    #region Protected Properties 

    protected AggregateCatalog AggregateCatalog { get; set; } 

    protected CompositionContainer CompositionContainer 
    { 
    get { return _container ?? (_container = CreateContainer()); } 
    } 

    protected static ILoggerFacade Logger { get; private set; } 

    protected IModuleCatalog ModuleCatalog 
    { 
    get { return _moduleCatalog ?? (_moduleCatalog = CreateModuleCatalog()); } 
    } 

    protected IEnumerable<object> TestEntities 
    { 
    get { return _testEntities ?? (_testEntities = GetTestEntities()); } 
    } 

    protected IEnumerable<ModuleInfo> TestModuleInformation 
    { 
    get { return _testModuleInformation ?? (_testModuleInformation = GetTestModuleInformation()); } 
    } 
    #endregion 
} 

Этот тест базового класса теперь подражает в какой-то степени, что обычно идет дальше в boostrapper, когда приложение обычно начинается. Тесты (разрешения) в каждой из надстроек теперь должны только предоставить список (экспортированных) расширений контейнеров и информацию о модуле для модуля Prism, который представляет надстройку (в дополнение к фактическим испытаниям на разрешение!)

Смежные вопросы