Files
Umbraco-CMS/src/Umbraco.Tests/Testing/UmbracoTestBase.cs

422 lines
17 KiB
C#
Raw Normal View History

using System;
using System.IO;
using System.Linq;
2016-11-05 19:23:55 +01:00
using System.Reflection;
using AutoMapper;
using Examine;
2016-11-05 19:23:55 +01:00
using LightInject;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Components;
2017-05-30 15:46:25 +02:00
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionRoots;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
2018-06-29 14:25:48 +02:00
using Umbraco.Core.IO.MediaPathSchemes;
2016-11-05 19:23:55 +01:00
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
2018-04-28 09:55:36 +02:00
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.PropertyEditors;
2017-06-23 18:54:42 +02:00
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
2018-04-28 09:55:36 +02:00
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
2016-12-16 10:40:14 +01:00
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
2016-11-07 20:50:14 +01:00
using Umbraco.Web;
using Umbraco.Web.Services;
using Umbraco.Examine;
using Umbraco.Tests.Testing.Objects.Accessors;
2017-09-27 11:35:51 +02:00
using Umbraco.Web.Composing.CompositionRoots;
2017-09-20 20:06:46 +02:00
using Umbraco.Web._Legacy.Actions;
2017-05-30 15:46:25 +02:00
using Current = Umbraco.Core.Composing.Current;
using Umbraco.Web.Routing;
2016-11-05 19:23:55 +01:00
2016-12-16 10:40:14 +01:00
namespace Umbraco.Tests.Testing
2016-11-05 19:23:55 +01:00
{
/// <summary>
/// Provides the top-level base class for all Umbraco integration tests.
/// </summary>
/// <remarks>
/// True unit tests do not need to inherit from this class, but most of Umbraco tests
/// are not true unit tests but integration tests requiring services, databases, etc. This class
/// provides all the necessary environment, through DI. Yes, DI is bad in tests - unit tests.
/// But it is OK in integration tests.
/// </remarks>
public abstract class UmbracoTestBase
{
// this class
// ensures that Current is properly resetted
// ensures that a service container is properly initialized and disposed
// compose the required dependencies according to test options (UmbracoTestAttribute)
//
// everything is virtual (because, why not?)
// starting a test runs like this:
// - SetUp() // when overriding, call base.SetUp() *first* then setup your own stuff
// --- Compose() // when overriding, call base.Commpose() *first* then compose your own stuff
// --- Initialize() // same
2016-11-05 19:23:55 +01:00
// - test runs
// - TearDown() // when overriding, clear you own stuff *then* call base.TearDown()
//
// about attributes
//
// this class defines the SetUp and TearDown methods, with proper attributes, and
// these attributes are *inherited* so classes inheriting from this class should *not*
// add the attributes to SetUp nor TearDown again
//
// this class is *not* marked with the TestFeature attribute because it is *not* a
// test feature, and no test "base" class should be. only actual test feature classes
// should be marked with that attribute.
protected ServiceContainer Container { get; private set; }
protected UmbracoTestAttribute Options { get; private set; }
2016-12-16 10:40:14 +01:00
protected static bool FirstTestInSession = true;
protected bool FirstTestInFixture = true;
2016-12-14 18:40:16 +01:00
internal TestObjects TestObjects { get; private set; }
2017-05-30 15:33:13 +02:00
private static TypeLoader _commonTypeLoader;
private TypeLoader _featureTypeLoader;
2016-11-05 19:23:55 +01:00
#region Accessors
protected ILogger Logger => Container.GetInstance<ILogger>();
protected IProfiler Profiler => Container.GetInstance<IProfiler>();
2018-05-16 10:06:51 +02:00
protected virtual ProfilingLogger ProfilingLogger => Container.GetInstance<ProfilingLogger>();
protected CacheHelper CacheHelper => Container.GetInstance<CacheHelper>();
protected virtual ISqlSyntaxProvider SqlSyntax => Container.GetInstance<ISqlSyntaxProvider>();
protected IMapperCollection Mappers => Container.GetInstance<IMapperCollection>();
#endregion
#region Setup
2016-11-05 19:23:55 +01:00
[SetUp]
public virtual void SetUp()
{
// should not need this if all other tests were clean
// but hey, never know, better avoid garbage-in
Reset();
Container = new ServiceContainer();
Container.ConfigureUmbracoCore();
2016-12-14 18:40:16 +01:00
TestObjects = new TestObjects(Container);
2016-11-05 19:23:55 +01:00
// get/merge the attributes marking the method and/or the classes
2017-09-19 18:47:07 +02:00
Options = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
2016-11-05 19:23:55 +01:00
Compose();
Initialize();
}
protected virtual void Compose()
{
ComposeLogging(Options.Logger);
ComposeCacheHelper();
ComposeAutoMapper(Options.AutoMapper);
2017-05-30 15:33:13 +02:00
ComposePluginManager(Options.PluginManager);
2016-11-05 19:23:55 +01:00
ComposeDatabase(Options.Database);
ComposeApplication(Options.WithApplication);
2017-09-20 20:06:46 +02:00
2016-11-05 19:23:55 +01:00
// etc
ComposeWeb();
2016-12-16 10:40:14 +01:00
ComposeWtf();
2016-11-05 19:23:55 +01:00
// not sure really
var composition = new Composition(Container, RuntimeLevel.Run);
Compose(composition);
}
protected virtual void Compose(Composition composition)
{ }
protected virtual void Initialize()
{
InitializeAutoMapper(Options.AutoMapper);
InitializeApplication(Options.WithApplication);
2016-11-05 19:23:55 +01:00
}
#endregion
#region Compose
2016-11-05 19:23:55 +01:00
protected virtual void ComposeLogging(UmbracoTestOptions.Logger option)
{
if (option == UmbracoTestOptions.Logger.Mock)
{
Container.RegisterSingleton(f => Mock.Of<ILogger>());
Container.RegisterSingleton(f => Mock.Of<IProfiler>());
}
else if (option == UmbracoTestOptions.Logger.Serilog)
2016-11-05 19:23:55 +01:00
{
Container.RegisterSingleton<ILogger>(f => new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config"))));
2016-11-05 19:23:55 +01:00
Container.RegisterSingleton<IProfiler>(f => new LogProfiler(f.GetInstance<ILogger>()));
}
Container.RegisterSingleton(f => new ProfilingLogger(f.GetInstance<ILogger>(), f.GetInstance<IProfiler>()));
}
protected virtual void ComposeWeb()
2016-12-16 10:40:14 +01:00
{
//TODO: Should we 'just' register the WebRuntimeComponent?
2016-12-16 10:40:14 +01:00
// imported from TestWithSettingsBase
// which was inherited by TestWithApplicationBase so pretty much used everywhere
2017-05-30 18:13:11 +02:00
Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor();
2018-07-18 10:32:50 +02:00
// web
Container.Register(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor);
Container.RegisterSingleton<PublishedRouter>();
Container.RegisterCollectionBuilder<ContentFinderCollectionBuilder>();
Container.Register<IContentLastChanceFinder, TestLastChanceFinder>();
Container.Register<IVariationContextAccessor, TestVariationContextAccessor>();
}
protected virtual void ComposeWtf()
2018-07-18 10:32:50 +02:00
{
2017-09-20 20:06:46 +02:00
// what else?
var runtimeStateMock = new Mock<IRuntimeState>();
runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run);
Container.RegisterSingleton(f => runtimeStateMock.Object);
// ah...
Container.RegisterCollectionBuilder<ActionCollectionBuilder>()
.SetProducer(Enumerable.Empty<Type>);
Container.RegisterCollectionBuilder<PropertyValueConverterCollectionBuilder>();
2017-10-17 17:43:15 +02:00
Container.RegisterSingleton<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
2018-06-29 14:25:48 +02:00
Container.RegisterSingleton<IMediaPathScheme, OriginalMediaPathScheme>();
2016-12-16 10:40:14 +01:00
}
2016-11-05 19:23:55 +01:00
protected virtual void ComposeCacheHelper()
{
Container.RegisterSingleton(f => CacheHelper.CreateDisabledCacheHelper());
Container.RegisterSingleton(f => f.GetInstance<CacheHelper>().RuntimeCache);
}
protected virtual void ComposeAutoMapper(bool configure)
{
if (configure == false) return;
2017-07-19 13:42:47 +02:00
Container.RegisterFrom<CoreMappingProfilesCompositionRoot>();
Container.RegisterFrom<WebMappingProfilesCompositionRoot>();
2016-11-05 19:23:55 +01:00
}
2017-05-30 15:33:13 +02:00
protected virtual void ComposePluginManager(UmbracoTestOptions.PluginManager pluginManager)
2016-11-05 19:23:55 +01:00
{
Container.RegisterSingleton(f =>
{
2017-05-30 15:33:13 +02:00
switch (pluginManager)
2016-11-05 19:23:55 +01:00
{
2017-05-30 15:33:13 +02:00
case UmbracoTestOptions.PluginManager.Default:
return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonPluginManager(f));
case UmbracoTestOptions.PluginManager.PerFixture:
return _featureTypeLoader ?? (_featureTypeLoader = CreatePluginManager(f));
case UmbracoTestOptions.PluginManager.PerTest:
return CreatePluginManager(f);
default:
throw new ArgumentOutOfRangeException(nameof(pluginManager));
}
2016-11-05 19:23:55 +01:00
});
}
2017-05-30 15:33:13 +02:00
protected virtual TypeLoader CreatePluginManager(IServiceFactory f)
{
return CreateCommonPluginManager(f);
}
private static TypeLoader CreateCommonPluginManager(IServiceFactory f)
{
return new TypeLoader(f.GetInstance<CacheHelper>().RuntimeCache, f.GetInstance<IGlobalSettings>(), f.GetInstance<ProfilingLogger>(), false)
2017-05-30 15:33:13 +02:00
{
AssembliesToScan = new[]
{
Assembly.Load("Umbraco.Core"),
2017-06-27 16:35:29 +02:00
Assembly.Load("Umbraco.Web"),
2017-06-23 18:54:42 +02:00
Assembly.Load("Umbraco.Tests")
2017-05-30 15:33:13 +02:00
}
};
}
2016-11-05 19:23:55 +01:00
protected virtual void ComposeDatabase(UmbracoTestOptions.Database option)
{
if (option == UmbracoTestOptions.Database.None) return;
// create the file
// create the schema
}
protected virtual void ComposeApplication(bool withApplication)
{
if (withApplication == false) return;
var umbracoSettings = SettingsForTests.GetDefaultUmbracoSettings();
var globalSettings = SettingsForTests.GetDefaultGlobalSettings();
//apply these globally
SettingsForTests.ConfigureSettings(umbracoSettings);
SettingsForTests.ConfigureSettings(globalSettings);
// default Datalayer/Repositories/SQL/Database/etc...
Container.RegisterFrom<RepositoryCompositionRoot>();
// register basic stuff that might need to be there for some container resolvers to work
Container.RegisterSingleton(factory => umbracoSettings);
Container.RegisterSingleton(factory => globalSettings);
Container.RegisterSingleton(factory => umbracoSettings.Content);
Container.RegisterSingleton(factory => umbracoSettings.Templates);
Container.RegisterSingleton(factory => umbracoSettings.WebRouting);
Container.Register(factory => new MediaFileSystem(Mock.Of<IFileSystem>()));
Container.RegisterSingleton<IExamineManager>(factory => ExamineManager.Instance);
// replace some stuff
Container.RegisterSingleton(factory => Mock.Of<IFileSystem>(), "ScriptFileSystem");
Container.RegisterSingleton(factory => Mock.Of<IFileSystem>(), "PartialViewFileSystem");
Container.RegisterSingleton(factory => Mock.Of<IFileSystem>(), "PartialViewMacroFileSystem");
Container.RegisterSingleton(factory => Mock.Of<IFileSystem>(), "StylesheetFileSystem");
// need real file systems here as templates content is on-disk only
//Container.RegisterSingleton<IFileSystem>(factory => Mock.Of<IFileSystem>(), "MasterpageFileSystem");
//Container.RegisterSingleton<IFileSystem>(factory => Mock.Of<IFileSystem>(), "ViewFileSystem");
Container.RegisterSingleton<IFileSystem>(factory => new PhysicalFileSystem("Views", "/views"), "ViewFileSystem");
Container.RegisterSingleton<IFileSystem>(factory => new PhysicalFileSystem("MasterPages", "/masterpages"), "MasterpageFileSystem");
Container.RegisterSingleton<IFileSystem>(factory => new PhysicalFileSystem("Xslt", "/xslt"), "XsltFileSystem");
// no factory (noop)
2017-09-26 14:57:50 +02:00
Container.RegisterSingleton<IPublishedModelFactory, NoopPublishedModelFactory>();
// register application stuff (database factory & context, services...)
Container.RegisterCollectionBuilder<MapperCollectionBuilder>()
.AddCore();
Container.RegisterSingleton<IEventMessagesFactory>(_ => new TransientEventMessagesFactory());
var sqlSyntaxProviders = TestObjects.GetDefaultSqlSyntaxProviders(Logger);
Container.RegisterSingleton<ISqlSyntaxProvider>(_ => sqlSyntaxProviders.OfType<SqlCeSyntaxProvider>().First());
2016-12-16 14:18:37 +01:00
Container.RegisterSingleton<IUmbracoDatabaseFactory>(f => new UmbracoDatabaseFactory(
2017-05-12 14:49:44 +02:00
Constants.System.UmbracoConnectionName,
sqlSyntaxProviders,
2017-05-12 14:49:44 +02:00
Logger,
Mock.Of<IMapperCollection>()));
2017-09-22 18:28:21 +02:00
Container.RegisterSingleton(f => f.TryGetInstance<IUmbracoDatabaseFactory>().SqlContext);
Container.RegisterCollectionBuilder<UrlSegmentProviderCollectionBuilder>(); // empty
2017-06-23 18:54:42 +02:00
Container.RegisterSingleton(factory => new FileSystems(factory.TryGetInstance<ILogger>()));
Container.RegisterSingleton(factory
=> TestObjects.GetScopeProvider(factory.TryGetInstance<ILogger>(), factory.TryGetInstance<FileSystems>(), factory.TryGetInstance<IUmbracoDatabaseFactory>()));
Container.RegisterSingleton(factory => (IScopeAccessor) factory.GetInstance<IScopeProvider>());
Container.RegisterFrom<ServicesCompositionRoot>();
// composition root is doing weird things, fix
Container.RegisterSingleton<IApplicationTreeService, ApplicationTreeService>();
Container.RegisterSingleton<ISectionService, SectionService>();
// somehow property editor ends up wanting this
2018-03-16 09:06:44 +01:00
Container.RegisterCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
2018-01-19 19:08:12 +01:00
Container.RegisterSingleton<ManifestParser>();
// note - don't register collections, use builders
2018-02-16 12:00:45 +01:00
Container.RegisterCollectionBuilder<DataEditorCollectionBuilder>();
Container.RegisterSingleton<PropertyEditorCollection>();
Container.RegisterSingleton<ParameterEditorCollection>();
}
2016-11-05 19:23:55 +01:00
#endregion
#region Initialize
protected virtual void InitializeAutoMapper(bool configure)
{
if (configure == false) return;
Mapper.Initialize(configuration =>
{
2017-07-19 13:42:47 +02:00
var profiles = Container.GetAllInstances<Profile>();
foreach (var profile in profiles)
configuration.AddProfile(profile);
2016-11-05 19:23:55 +01:00
});
}
protected virtual void InitializeApplication(bool withApplication)
{
if (withApplication == false) return;
TestHelper.InitializeContentDirectories();
}
2016-11-05 19:23:55 +01:00
#endregion
#region TearDown and Reset
[TearDown]
public virtual void TearDown()
{
2016-12-16 10:40:14 +01:00
FirstTestInFixture = false;
FirstTestInSession = false;
2016-11-05 19:23:55 +01:00
Reset();
if (Options.WithApplication)
{
TestHelper.CleanContentDirectories();
TestHelper.CleanUmbracoSettingsConfig();
}
2016-11-05 19:23:55 +01:00
}
protected virtual void Reset()
{
2017-06-23 18:54:42 +02:00
// reset and dispose scopes
// ensures we don't leak an opened database connection
// which would lock eg SqlCe .sdf files
if (Container?.TryGetInstance<IScopeProvider>() is ScopeProvider scopeProvider)
2017-06-23 18:54:42 +02:00
{
Core.Scoping.Scope scope;
while ((scope = scopeProvider.AmbientScope) != null)
{
scope.Reset();
scope.Dispose();
}
}
2016-11-05 19:23:55 +01:00
Current.Reset();
Container?.Dispose();
Container = null;
2016-11-07 20:50:14 +01:00
// reset all other static things that should not be static ;(
UriUtility.ResetAppDomainAppVirtualPath();
2016-12-16 10:40:14 +01:00
SettingsForTests.Reset(); // fixme - should it be optional?
2018-03-30 19:31:42 +02:00
Mapper.Reset();
2018-04-28 09:55:36 +02:00
// clear static events
DocumentRepository.ClearScopeEvents();
MediaRepository.ClearScopeEvents();
MemberRepository.ClearScopeEvents();
ContentTypeService.ClearScopeEvents();
MediaTypeService.ClearScopeEvents();
MemberTypeService.ClearScopeEvents();
2016-11-05 19:23:55 +01:00
}
#endregion
}
}