From 76cda7fd0cc758c1ad15bfd5c94932b18da83e22 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Fri, 11 Feb 2022 14:24:45 +0000 Subject: [PATCH] Fix issue running UmbracoTestServerTestBase tests with debugger attached Ensure ConfigureWebServer called before ConfigureServices for tests that make use of WebApplicationFactory. Still not apparent why tests would pass if debugger isn't attached? --- .../UmbracoTestServerTestBase.cs | 70 +++++++++++++------ .../Testing/UmbracoIntegrationTest.cs | 14 ++-- .../Events/EventAggregatorTests.cs | 3 +- .../UmbracoExamine/ExamineBaseTest.cs | 7 +- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index bd9418bdb6..45fb305de9 100644 --- a/tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -12,13 +12,16 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.DependencyInjection; @@ -77,28 +80,55 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest LinkGenerator = Factory.Services.GetRequiredService(); } - public override IHostBuilder CreateHostBuilder() + protected override IHostBuilder CreateHostBuilder() { - IHostBuilder builder = base.CreateHostBuilder(); - builder.ConfigureWebHost(builder => - { - // need to configure the IWebHostEnvironment too - builder.ConfigureServices((c, s) => c.HostingEnvironment = TestHelper.GetWebHostEnvironment()); + /* It is important that ConfigureWebHost is called before ConfigureServices, this is consistent with the host setup + * found in Program.cs and avoids nasty surprises. + * + * e.g. the registration for RefreshingRazorViewEngine requires that IWebHostEnvironment is registered + * at the point in time that the service collection is snapshotted. + */ + IHostBuilder hostBuilder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((context, configBuilder) => + { + context.HostingEnvironment = TestHelper.GetWebHostEnvironment(); + configBuilder.Sources.Clear(); + configBuilder.AddInMemoryCollection(InMemoryConfiguration); - // call startup - builder.Configure(app => Configure(app)); - }) - .UseDefaultServiceProvider(cfg => - { - // These default to true *if* WebHostEnvironment.EnvironmentName == Development - // When running tests, EnvironmentName used to be null on the mock that we register into services. - // Enable opt in for tests so that validation occurs regardless of environment name. - // Would be nice to have this on for UmbracoIntegrationTest also but requires a lot more effort to resolve issues. - cfg.ValidateOnBuild = true; - cfg.ValidateScopes = true; - }); + Configuration = configBuilder.Build(); + }) + .ConfigureWebHost(builder => + { + // need to configure the IWebHostEnvironment too + builder.ConfigureServices((c, s) => c.HostingEnvironment = TestHelper.GetWebHostEnvironment()); - return builder; + // call startup + builder.Configure(app => Configure(app)); + }) + .ConfigureServices((_, services) => + { + ConfigureServices(services); + ConfigureTestSpecificServices(services); + services.AddUnique(CreateLoggerFactory()); + + if (!TestOptions.Boot) + { + // If boot is false, we don't want the CoreRuntime hosted service to start + // So we replace it with a Mock + services.AddUnique(Mock.Of()); + } + }) + .UseDefaultServiceProvider(cfg => + { + // These default to true *if* WebHostEnvironment.EnvironmentName == Development + // When running tests, EnvironmentName used to be null on the mock that we register into services. + // Enable opt in for tests so that validation occurs regardless of environment name. + // Would be nice to have this on for UmbracoIntegrationTest also but requires a lot more effort to resolve issues. + cfg.ValidateOnBuild = true; + cfg.ValidateScopes = true; + }); + + return hostBuilder; } /// @@ -157,7 +187,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest protected WebApplicationFactory Factory { get; private set; } - public override void ConfigureServices(IServiceCollection services) + private void ConfigureServices(IServiceCollection services) { services.AddTransient(); diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 7ec8a7fc14..915d2d7629 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -128,7 +128,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing UseTestDatabase(Services); } - private ILoggerFactory CreateLoggerFactory() + protected ILoggerFactory CreateLoggerFactory() { try { @@ -163,7 +163,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing /// /// Create the Generic Host and execute startup ConfigureServices/Configure calls /// - public virtual IHostBuilder CreateHostBuilder() + protected virtual IHostBuilder CreateHostBuilder() { IHostBuilder hostBuilder = Host.CreateDefaultBuilder() @@ -180,9 +180,10 @@ namespace Umbraco.Cms.Tests.Integration.Testing Configuration = configBuilder.Build(); }) - .ConfigureServices((hostContext, services) => + .ConfigureServices((_, services) => { ConfigureServices(services); + ConfigureTestSpecificServices(services); services.AddUnique(CreateLoggerFactory()); if (!TestOptions.Boot) @@ -192,10 +193,15 @@ namespace Umbraco.Cms.Tests.Integration.Testing services.AddUnique(Mock.Of()); } }); + return hostBuilder; } - public virtual void ConfigureServices(IServiceCollection services) + protected virtual void ConfigureTestSpecificServices(IServiceCollection services) + { + } + + private void ConfigureServices(IServiceCollection services) { services.AddSingleton(TestHelper.DbProviderFactoryCreator); services.AddTransient(); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Events/EventAggregatorTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Events/EventAggregatorTests.cs index 6446bf542a..55429af147 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Events/EventAggregatorTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Events/EventAggregatorTests.cs @@ -14,9 +14,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Events [TestFixture] public class EventAggregatorTests : UmbracoTestServerTestBase { - public override void ConfigureServices(IServiceCollection services) + protected override void ConfigureTestSpecificServices(IServiceCollection services) { - base.ConfigureServices(services); services.AddScoped(); services.AddTransient, EventAggregatorTestNotificationHandler>(); } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs index bc1c6b8f02..7769f99d83 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs @@ -29,11 +29,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine protected IRuntimeState RunningRuntimeState { get; } = Mock.Of(x => x.Level == RuntimeLevel.Run); - public override void ConfigureServices(IServiceCollection services) - { - base.ConfigureServices(services); - services.AddSingleton(); - } + protected override void ConfigureTestSpecificServices(IServiceCollection services) + => services.AddSingleton(); /// /// Used to create and manage a testable index