Skip to content

Commit

Permalink
collection fixture support
Browse files Browse the repository at this point in the history
implements #673
  • Loading branch information
dj-nitehawk committed May 6, 2024
1 parent 7394b26 commit db87f96
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Src/Directory.Build.props
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>

<Version>5.25.0.2-beta</Version>
<Version>5.25.0.3-beta</Version>

<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
Expand Down
9 changes: 9 additions & 0 deletions Src/Library/changelog.md
Expand Up @@ -49,6 +49,15 @@ todo:

</details>

<details><summary>Test collection fixture support</summary>

todo:

- write tests
- write docs

</details>

[//]: # (## Improvements 🚀)

[//]: # (## Fixes 🪲)
Expand Down
23 changes: 12 additions & 11 deletions Src/Testing/FixtureFramework/TestAssemblyRunner.cs
Expand Up @@ -16,33 +16,34 @@ class TestAssemblyRunner(ITestAssembly testAssembly,
protected override async Task AfterTestAssemblyStartingAsync()
{
// Let everything initialize
await base.AfterTestAssemblyStartingAsync().ConfigureAwait(false);
await base.AfterTestAssemblyStartingAsync();

// Go find all the AssemblyFixtureAttributes adorned on the test assembly
await Aggregator.RunAsync(
async () =>
{
ISet<Type> assemblyFixtures = new HashSet<Type>(
((IReflectionAssemblyInfo)TestAssembly.Assembly).Assembly
.GetTypes()
.Select(type => type.GetInterfaces())
.SelectMany(x => x)
.Where(@interface => @interface.IsAssignableToGenericType(typeof(IAssemblyFixture<>)))
.ToArray());
var assemblyFixtures = new HashSet<Type>(
((IReflectionAssemblyInfo)TestAssembly.Assembly)
.Assembly
.GetTypes()
.Select(type => type.GetInterfaces())
.SelectMany(x => x)
.Where(@interface => @interface.IsAssignableToGenericType(typeof(IAssemblyFixture<>)))
.ToArray());

// Instantiate all the fixtures
foreach (var fixtureAttribute in assemblyFixtures)
{
var fixtureType = fixtureAttribute.GetGenericArguments()[0];
var hasConstructorWithMessageSink = fixtureType.GetConstructor(new[] { typeof(IMessageSink) }) != null;
var hasConstructorWithMessageSink = fixtureType.GetConstructor([typeof(IMessageSink)]) != null;
_assemblyFixtureMappings[fixtureType] = hasConstructorWithMessageSink
? Activator.CreateInstance(fixtureType, ExecutionMessageSink)!
: Activator.CreateInstance(fixtureType)!;
}

// Initialize IAsyncLifetime fixtures
foreach (var asyncLifetime in _assemblyFixtureMappings.Values.OfType<IAsyncLifetime>())
await Aggregator.RunAsync(async () => await asyncLifetime.InitializeAsync().ConfigureAwait(false)).ConfigureAwait(false);
await Aggregator.RunAsync(async () => await asyncLifetime.InitializeAsync());
});
}

Expand All @@ -61,7 +62,7 @@ protected override async Task BeforeTestAssemblyFinishedAsync()
_assemblyFixtureMappings.Values.OfType<IAsyncLifetime>(),
async (disposable, _) => await Aggregator.RunAsync(async () => await disposable.DisposeAsync()));

await base.BeforeTestAssemblyFinishedAsync().ConfigureAwait(false);
await base.BeforeTestAssemblyFinishedAsync();
}

protected override async Task<RunSummary> RunTestCollectionAsync(IMessageBus messageBus,
Expand Down
3 changes: 1 addition & 2 deletions Src/Testing/FixtureFramework/TestFrameworkExecutor.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Reflection;
using System.Reflection;
using Xunit.Abstractions;
using Xunit.Sdk;

Expand Down
39 changes: 23 additions & 16 deletions Src/Testing/TestBase.cs
Expand Up @@ -9,26 +9,15 @@ namespace FastEndpoints.Testing;
/// abstract class for implementing a test-class, which is a collection of integration tests that may be related to each other.
/// test methods can be run in a given order by decorating the methods with <see cref="PriorityAttribute" />
/// </summary>
/// <typeparam name="TAppFixture">
/// <para>
/// the type of the app fixture. an app fixture is an implementation of <see cref="AppFixture{TProgram}" /> abstract class which is a uniquely configured running
/// instance of your application being tested (sut). the app fixture instance is created only once before any of the test methods are executed and torn down after all
/// test methods of the class have run. all test methods of the test-class will be accessing that same fixture instance per test run. the underlying WAF instance
/// however is cached and reused per each derived app fixture type in order to speed up test execution. i.e. it's recommended to use the same derived app fixture type
/// with multiple test-classes.
/// </para>
/// <para>
/// to share common state between multiple test-methods of the same test-class, you can inherit the <see cref="TestBase{TAppFixture,TState}" /> abstract class and
/// provide an additional "state fixture" for the test-class.
/// </para>
/// </typeparam>
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
public abstract class TestBase<TAppFixture> : IAsyncLifetime, IFaker, IClassFixture<TAppFixture> where TAppFixture : BaseFixture
public abstract class TestBase : IAsyncLifetime, IFaker
{
static readonly Faker _faker = new();

public Faker Fake => _faker;

// ReSharper disable VirtualMemberNeverOverridden.Global

/// <summary>
/// override this method if you'd like to do some one-time setup for the test-class.
/// it is run before any of the test-methods of the class is executed.
Expand All @@ -50,6 +39,25 @@ Task IAsyncLifetime.DisposeAsync()
=> TearDownAsync();
}

/// <summary>
/// abstract class for implementing a test-class, which is a collection of integration tests that may be related to each other.
/// test methods can be run in a given order by decorating the methods with <see cref="PriorityAttribute" />
/// </summary>
/// <typeparam name="TAppFixture">
/// <para>
/// the type of the app fixture. an app fixture is an implementation of <see cref="AppFixture{TProgram}" /> abstract class which is a uniquely configured running
/// instance of your application being tested (sut). the app fixture instance is created only once before any of the test methods are executed and torn down after all
/// test methods of the class have run. all test methods of the test-class will be accessing that same fixture instance per test run. the underlying WAF instance
/// however is cached and reused per each derived app fixture type in order to speed up test execution. i.e. it's recommended to use the same derived app fixture type
/// with multiple test-classes.
/// </para>
/// <para>
/// to share common state between multiple test-methods of the same test-class, you can inherit the <see cref="TestBase{TAppFixture,TState}" /> abstract class and
/// provide an additional "state fixture" for the test-class.
/// </para>
/// </typeparam>
public abstract class TestBase<TAppFixture> : TestBase, IClassFixture<TAppFixture> where TAppFixture : BaseFixture;

/// <summary>
/// abstract class for implementing a test-class, which is a collection of integration tests that may be related to each other.
/// test methods can be run in a given order by decorating the methods with <see cref="PriorityAttribute" />
Expand All @@ -62,8 +70,7 @@ Task IAsyncLifetime.DisposeAsync()
/// with multiple test-classes.
/// </typeparam>
/// <typeparam name="TState">the type of the shared state fixture. implement a "state fixture" by inheriting <see cref="StateFixture" /> abstract class.</typeparam>
public abstract class TestBase<TAppFixture, TState> : TestBase<TAppFixture>, IClassFixture<TState>
where TAppFixture : BaseFixture where TState : StateFixture;
public abstract class TestBase<TAppFixture, TState> : TestBase<TAppFixture>, IClassFixture<TState> where TAppFixture : BaseFixture where TState : StateFixture;

[Obsolete("Use the TestBase<TAppFixture> class going forward. This class will be removed at the next major version jump.")]
public abstract class TestClass<TAppFixture>(TAppFixture a, ITestOutputHelper o) : TestBase<TAppFixture> where TAppFixture : BaseFixture
Expand Down
10 changes: 10 additions & 0 deletions Src/Testing/TestCollection.cs
@@ -0,0 +1,10 @@
using Xunit;

namespace FastEndpoints.Testing;

/// <summary>
/// abstract class for creating a collection definition
/// </summary>
/// <typeparam name="TAppFixture">the type of the app fixture that will last for the full lifetime of the test collection</typeparam>
public abstract class TestCollection<TAppFixture> : ICollectionFixture<TAppFixture>
where TAppFixture : BaseFixture { }

0 comments on commit db87f96

Please sign in to comment.