From cbc7c2d461abf0f84c1e7d89c1cc151ccdc7c86b Mon Sep 17 00:00:00 2001 From: Craig Fowler Date: Sat, 6 Mar 2021 13:55:19 +0000 Subject: [PATCH] WIP #552 - Refactor test logic (reusability) This refactor of integration test logic is to boost its reusability. Moving some functionality to attributes & customization classes means I can reuse elsewhere without duplication. --- ...lsaAndCommonTemporalActivitiesAttribute.cs | 21 ++++++++ ...HostBuilderWithElsaAndHangfireAttribute.cs | 21 ++++++++ .../HostBuilderWithElsaAndQuartzAttribute.cs | 20 ++++++++ ...emporalServiceCollectionExtensionsTests.cs | 48 +++++-------------- .../HostBubilderUsingServicesCustomization.cs | 37 ++++++++++++++ ...tofixtureServiceProviderSpecimenBuilder.cs | 4 +- ...ostBubilderUsingServicesSpecimenBuilder.cs | 23 +++++++++ .../SpecimenBuilders/RequestExtensions.cs | 29 +++++++++++ .../Elsa.Testing.Shared.csproj | 1 + 9 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndCommonTemporalActivitiesAttribute.cs create mode 100644 test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndHangfireAttribute.cs create mode 100644 test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndQuartzAttribute.cs create mode 100644 test/shared/Elsa.Testing.Shared/AutoFixture/Customizations/HostBubilderUsingServicesCustomization.cs create mode 100644 test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/HostBubilderUsingServicesSpecimenBuilder.cs create mode 100644 test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/RequestExtensions.cs diff --git a/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndCommonTemporalActivitiesAttribute.cs b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndCommonTemporalActivitiesAttribute.cs new file mode 100644 index 000000000..67a714969 --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndCommonTemporalActivitiesAttribute.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using AutoFixture; +using AutoFixture.Xunit2; +using Elsa.Activities.Temporal; +using Elsa.Testing.Shared.AutoFixture.Customizations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Core.IntegrationTests.Extensions +{ + public class HostBuilderWithElsaAndCommonTemporalActivitiesAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services.AddElsa(elsa => { + CommonTemporalActivityServices.AddCommonTemporalActivities(elsa); + }); + }, parameter); + } + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndHangfireAttribute.cs b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndHangfireAttribute.cs new file mode 100644 index 000000000..5ce7d0a3a --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndHangfireAttribute.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using AutoFixture; +using AutoFixture.Xunit2; +using Elsa.Testing.Shared.AutoFixture.Customizations; +using Hangfire; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Core.IntegrationTests.Extensions +{ + public class HostBuilderWithElsaAndHangfireAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services.AddElsa(elsa => { + elsa.AddHangfireTemporalActivities(config => config.UseInMemoryStorage()); + }); + }, parameter); + } + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndQuartzAttribute.cs b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndQuartzAttribute.cs new file mode 100644 index 000000000..81e492d0b --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithElsaAndQuartzAttribute.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using AutoFixture; +using AutoFixture.Xunit2; +using Elsa.Testing.Shared.AutoFixture.Customizations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Core.IntegrationTests.Extensions +{ + public class HostBuilderWithElsaAndQuartzAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services.AddElsa(elsa => { + elsa.AddQuartzTemporalActivities(); + }); + }, parameter); + } + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Extensions/TemporalServiceCollectionExtensionsTests.cs b/test/integration/Elsa.Core.IntegrationTests/Extensions/TemporalServiceCollectionExtensionsTests.cs index ddf5eabf2..58bb9b76c 100644 --- a/test/integration/Elsa.Core.IntegrationTests/Extensions/TemporalServiceCollectionExtensionsTests.cs +++ b/test/integration/Elsa.Core.IntegrationTests/Extensions/TemporalServiceCollectionExtensionsTests.cs @@ -6,20 +6,15 @@ using Hangfire; using System.Threading.Tasks; using System.Threading; using Elsa.Activities.Temporal; +using Elsa.Core.IntegrationTests.Extensions; namespace Elsa.UnitTests.Extensions { public class TemporalServiceCollectionExtensionsTests { - [Fact(DisplayName = "Starting a hosted app which uses only AddCommonTemporalActivities should throw InvalidOperationException because of the missing impl")] - public void AddCommonTemporalActivitiesThrowsDuringStartupIfNoTemporalImplementationPresent() + [Theory(DisplayName = "Starting a hosted app which uses only AddCommonTemporalActivities should throw InvalidOperationException because of the missing impl"), AutoMoqData] + public void AddCommonTemporalActivitiesThrowsDuringStartupIfNoTemporalImplementationPresent([HostBuilderWithElsaAndCommonTemporalActivities] IHostBuilder hostBuilder) { - var hostBuilder = CreateHostBuilderUsingServices(services => { - services.AddElsa(elsa => { - CommonTemporalActivityServices.AddCommonTemporalActivities(elsa); - }); - }); - var cancellationSource = new CancellationTokenSource(); try { @@ -33,15 +28,9 @@ namespace Elsa.UnitTests.Extensions } } - [Fact(DisplayName = "Starting a hosted app which uses AddHangfireTemporalActivities should not throw")] - public void AddHangfireTemporalActivitiesDoesNotThrowDuringStartup() + [Theory(DisplayName = "Starting a hosted app which uses AddHangfireTemporalActivities should not throw"), AutoMoqData] + public void AddHangfireTemporalActivitiesDoesNotThrowDuringStartup([HostBuilderWithElsaAndHangfire] IHostBuilder hostBuilder) { - var hostBuilder = CreateHostBuilderUsingServices(services => { - services.AddElsa(elsa => { - elsa.AddHangfireTemporalActivities(config => config.UseInMemoryStorage()); - }); - }); - var cancellationSource = new CancellationTokenSource(); try { @@ -57,15 +46,9 @@ namespace Elsa.UnitTests.Extensions } } - [Fact(DisplayName = "Starting a hosted app which uses AddQuartzTemporalActivities should not throw")] - public void AddQuartzTemporalActivitiesDoesNotThrowDuringStartup() + [Theory(DisplayName = "Starting a hosted app which uses AddQuartzTemporalActivities should not throw"), AutoMoqData] + public void AddQuartzTemporalActivitiesDoesNotThrowDuringStartup([HostBuilderWithElsaAndQuartz] IHostBuilder hostBuilder) { - var hostBuilder = CreateHostBuilderUsingServices(services => { - services.AddElsa(elsa => { - elsa.AddQuartzTemporalActivities(); - }); - }); - var cancellationSource = new CancellationTokenSource(); try { @@ -81,15 +64,6 @@ namespace Elsa.UnitTests.Extensions } } - /// - /// Convenience method creates an using the specified services. - /// - /// Services to use in the created host builder - /// A host builder - static IHostBuilder CreateHostBuilderUsingServices(Action serviceConfig) - => Host.CreateDefaultBuilder() - .ConfigureServices((hostBuilder, services) => serviceConfig(services)); - /// /// Starts up the specified instance using console lifetime. /// The current thread is then blocked for a short while, giving the app a chance to start up and run. @@ -111,10 +85,10 @@ namespace Elsa.UnitTests.Extensions /// The cancellation source static void CancelAndSquelchExceptions(CancellationTokenSource cancellationSource) { - try { cancellationSource.Cancel(); } - catch(Exception) { - // This is only ever used as teardown code, which is why we don't care about exceptions. - } + try { cancellationSource.Cancel(); } + catch(Exception) { + // This is only ever used as teardown code, which is why we don't care about exceptions. + } } } } \ No newline at end of file diff --git a/test/shared/Elsa.Testing.Shared/AutoFixture/Customizations/HostBubilderUsingServicesCustomization.cs b/test/shared/Elsa.Testing.Shared/AutoFixture/Customizations/HostBubilderUsingServicesCustomization.cs new file mode 100644 index 000000000..7f3d3fd72 --- /dev/null +++ b/test/shared/Elsa.Testing.Shared/AutoFixture/Customizations/HostBubilderUsingServicesCustomization.cs @@ -0,0 +1,37 @@ +using System; +using System.Reflection; +using AutoFixture; +using AutoFixture.Kernel; +using Elsa.Testing.Shared.AutoFixture.SpecimenBuilders; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Testing.Shared.AutoFixture.Customizations +{ + public class HostBubilderUsingServicesCustomization : ICustomization + { + readonly Action serviceConfig; + readonly ParameterInfo? parameter; + + public void Customize(IFixture fixture) + { + fixture.Customizations.Insert(0, GetSpecimenBuilder()); + } + + ISpecimenBuilder GetSpecimenBuilder() + { + var specimenBuilder = new HostBubilderUsingServicesSpecimenBuilder(serviceConfig); + if(parameter is null) return specimenBuilder; + + var paramSpec = new ParameterSpecification(parameter.ParameterType, parameter.Name); + return new FilteringSpecimenBuilder(specimenBuilder, paramSpec); + } + + public HostBubilderUsingServicesCustomization(Action serviceConfig, + ParameterInfo? parameter = null) + { + this.parameter = parameter ?? throw new ArgumentNullException(nameof(parameter)); + this.serviceConfig = serviceConfig ?? throw new ArgumentNullException(nameof(serviceConfig)); + + } + } +} \ No newline at end of file diff --git a/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/AutofixtureServiceProviderSpecimenBuilder.cs b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/AutofixtureServiceProviderSpecimenBuilder.cs index 4a124efa8..4acc74bff 100644 --- a/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/AutofixtureServiceProviderSpecimenBuilder.cs +++ b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/AutofixtureServiceProviderSpecimenBuilder.cs @@ -13,9 +13,7 @@ namespace Elsa.Testing.Shared.AutoFixture.SpecimenBuilders { public object Create(object request, ISpecimenContext context) { - if(Equals(request, typeof(IServiceProvider))) - return GetServiceProvider(context); - if(request is ParameterInfo paramInfo && paramInfo.ParameterType == typeof(IServiceProvider)) + if(request.IsAnAutofixtureRequestForType()) return GetServiceProvider(context); return new NoSpecimen(); diff --git a/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/HostBubilderUsingServicesSpecimenBuilder.cs b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/HostBubilderUsingServicesSpecimenBuilder.cs new file mode 100644 index 000000000..5e501da39 --- /dev/null +++ b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/HostBubilderUsingServicesSpecimenBuilder.cs @@ -0,0 +1,23 @@ +using System; +using AutoFixture.Kernel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Elsa.Testing.Shared.AutoFixture.SpecimenBuilders +{ + public class HostBubilderUsingServicesSpecimenBuilder : ISpecimenBuilder + { + readonly Action serviceConfig; + + public object Create(object request, ISpecimenContext context) + => request.IsAnAutofixtureRequestForType()? GetHostBuilder() : new NoSpecimen(); + + IHostBuilder GetHostBuilder() + => Host.CreateDefaultBuilder().ConfigureServices((hostBuilder, services) => serviceConfig(services)); + + public HostBubilderUsingServicesSpecimenBuilder(Action serviceConfig) + { + this.serviceConfig = serviceConfig; + } + } +} \ No newline at end of file diff --git a/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/RequestExtensions.cs b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/RequestExtensions.cs new file mode 100644 index 000000000..ab8427d75 --- /dev/null +++ b/test/shared/Elsa.Testing.Shared/AutoFixture/SpecimenBuilders/RequestExtensions.cs @@ -0,0 +1,29 @@ +using System.Reflection; + +namespace Elsa.Testing.Shared.AutoFixture.SpecimenBuilders +{ + /// + /// Convenience methods for specimen builders. You're only likey to want to use these extensions + /// within a specimen builder class. + /// + public static class AutofixtureRequestExtensions + { + /// + /// Gets a value which indicates if the specified qualifies as an Autofixture + /// request for an instance of the specified generic type. + /// + /// The request object + /// The desired specimen type + /// true if the is a request for an instance of ; false otherwise. + public static bool IsAnAutofixtureRequestForType(this object request) + { + if(Equals(request, typeof(T))) + return true; + + if(request is ParameterInfo paramInfo && paramInfo.ParameterType == typeof(T)) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj b/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj index 5df535cd8..18bcc5296 100644 --- a/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj +++ b/test/shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj @@ -19,6 +19,7 @@ +