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.
This commit is contained in:
parent
dc95276abb
commit
cbc7c2d461
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method creates an <see cref="IHostBuilder"/> using the specified services.
|
||||
/// </summary>
|
||||
/// <param name="serviceConfig">Services to use in the created host builder</param>
|
||||
/// <returns>A host builder</returns>
|
||||
static IHostBuilder CreateHostBuilderUsingServices(Action<IServiceCollection> serviceConfig)
|
||||
=> Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((hostBuilder, services) => serviceConfig(services));
|
||||
|
||||
/// <summary>
|
||||
/// Starts up the specified <paramref name="hostBuilder"/> 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
|
|||
/// <param name="cancellationSource">The cancellation source</param>
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IServiceCollection> 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<IServiceCollection> serviceConfig,
|
||||
ParameterInfo? parameter = null)
|
||||
{
|
||||
this.parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
|
||||
this.serviceConfig = serviceConfig ?? throw new ArgumentNullException(nameof(serviceConfig));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IServiceProvider>())
|
||||
return GetServiceProvider(context);
|
||||
|
||||
return new NoSpecimen();
|
||||
|
|
|
|||
|
|
@ -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<IServiceCollection> serviceConfig;
|
||||
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
=> request.IsAnAutofixtureRequestForType<IHostBuilder>()? GetHostBuilder() : new NoSpecimen();
|
||||
|
||||
IHostBuilder GetHostBuilder()
|
||||
=> Host.CreateDefaultBuilder().ConfigureServices((hostBuilder, services) => serviceConfig(services));
|
||||
|
||||
public HostBubilderUsingServicesSpecimenBuilder(Action<IServiceCollection> serviceConfig)
|
||||
{
|
||||
this.serviceConfig = serviceConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Elsa.Testing.Shared.AutoFixture.SpecimenBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenience methods for specimen builders. You're only likey to want to use these extensions
|
||||
/// within a specimen builder class.
|
||||
/// </summary>
|
||||
public static class AutofixtureRequestExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value which indicates if the specified <see cref="object"/> qualifies as an Autofixture
|
||||
/// request for an instance of the specified generic type.
|
||||
/// </summary>
|
||||
/// <param name="request">The request object</param>
|
||||
/// <typeparam name="T">The desired specimen type</typeparam>
|
||||
/// <returns><c>true</c> if the <paramref name="request"/> is a request for an instance of <typeparamref name="T"/>; <c>false</c> otherwise.</returns>
|
||||
public static bool IsAnAutofixtureRequestForType<T>(this object request)
|
||||
{
|
||||
if(Equals(request, typeof(T)))
|
||||
return true;
|
||||
|
||||
if(request is ParameterInfo paramInfo && paramInfo.ParameterType == typeof(T))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.3" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in a new issue