diff --git a/README.md b/README.md index eabf694..8af16f4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ # FEFF.TestFixtures +

+ FEFF.TestFixtures Logo +

+ [![Test](https://github.com/metacoder-feff/FEFF.TestFixtures/actions/workflows/test.yml/badge.svg)](https://github.com/metacoder-feff/FEFF.TestFixtures/actions/workflows/test.yml) [![Release](https://github.com/metacoder-feff/FEFF.TestFixtures/actions/workflows/release-nuget.yml/badge.svg)](https://github.com/metacoder-feff/FEFF.TestFixtures/actions/workflows/release-nuget.yml) Integrations: -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.XunitV3?label=FEFF.TestFixtures.XunitV3)](https://www.nuget.org/packages/FEFF.TestFixtures.XunitV3) -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.TUnit?label=FEFF.TestFixtures.TUnit)](https://www.nuget.org/packages/FEFF.TestFixtures.TUnit) -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.XunitV4?label=FEFF.TestFixtures.XunitV4)](https://www.nuget.org/packages/FEFF.TestFixtures.XunitV4) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.XunitV3?label=XunitV3)](https://www.nuget.org/packages/FEFF.TestFixtures.XunitV3) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.TUnit?label=TUnit)](https://www.nuget.org/packages/FEFF.TestFixtures.TUnit) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.XunitV4?label=XunitV4)](https://www.nuget.org/packages/FEFF.TestFixtures.XunitV4) Fixture libraries: [![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures?label=FEFF.TestFixtures)](https://www.nuget.org/packages/FEFF.TestFixtures) -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore?label=FEFF.TestFixtures.AspNetCore)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore) -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore.EF?label=FEFF.TestFixtures.AspNetCore.EF)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore.EF) -[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore.SignalR?label=FEFF.TestFixtures.AspNetCore.SignalR)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore.SignalR) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore?label=AspNetCore)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore.EF?label=AspNetCore.EF)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore.EF) +[![NuGet Version](https://img.shields.io/nuget/v/FEFF.TestFixtures.AspNetCore.SignalR?label=AspNetCore.SignalR)](https://www.nuget.org/packages/FEFF.TestFixtures.AspNetCore.SignalR) ✅ Replace setup/teardown methods and test-class "Disposable pattern" with reusable **Fixtures**. ✅ Fixtures can depend on other fixtures. diff --git a/docs/articles/overview.md b/docs/articles/overview.md index 08d08e4..d8fb184 100644 --- a/docs/articles/overview.md +++ b/docs/articles/overview.md @@ -1,6 +1,8 @@ # Overview -FEFF.TestFixtures is a testing library extension for .NET that replaces traditional setup/teardown methods or test-class "Disposable pattern" with reusable, composable fixtures. +![lLogo](../images/logo/logo.svg){width=250 height=120} + +FEFF.TestFixtures is a .NET testing library extension that replaces traditional setup/teardown methods or the test-class "Disposable pattern" with reusable, composable fixtures. ## What is a Fixture? @@ -16,19 +18,19 @@ A **fixture** is a reusable component that manages resources for testing purpose FEFF.TestFixtures aims to: -✅ **Eliminate boilerplate** - Replace repetitive setup/teardown methods -✅ **Enable composition** - Build complex fixtures from simpler ones -✅ **Simplify ASP.NET Core testing** - Built-in fixtures for web applications -✅ **Maintain isolation** - Each test gets clean, predictable state +✅ **Eliminate boilerplate** – Replace repetitive setup/teardown methods +✅ **Enable composition** – Build complex fixtures from simpler ones +✅ **Simplify ASP.NET Core testing** – Built-in fixtures for web applications +✅ **Maintain isolation** – Each test gets clean, predictable state -Additionaly: -✅ **Support multiple frameworks** - Works with xUnit v3 and TUnit +Additionally: +✅ **Support multiple frameworks** – Works with xUnit v3 and TUnit ## Key Concepts ### Fixtures -Fixtures are classes marked with the `[Fixture]` attribute. The framework discovers and manages their lifecycle automatically. +Fixtures are classes marked with the `[Fixture]` attribute. The framework automatically discovers and manages their lifecycle. ```csharp [Fixture] @@ -50,13 +52,13 @@ public class MyFixture : IDisposable ### Scopes -The **scope** of a fixture defines its lifetime. Within a scope, each fixture is created only once (lazily on demand) and destroyed at the end of the scope. If the fixture implements Dispose() or DisposeAsync(), those methods are called. +The **scope** of a fixture defines its lifetime. Within a scope, each fixture is created only once (lazily, on demand) and destroyed at the end of the scope. If the fixture implements `Dispose()` or `DisposeAsync()`, those methods are called. -The available scopes are defined by the test framework used. For **Xunit Integration**, they are: -`TestCase` , `Class`, `Collection`, `Assembly` +The available scopes are defined by the test framework used. For **xUnit Integration**, they are: +`TestCase`, `Class`, `Collection`, `Assembly` For **TUnit Integration**, they are: -`TestCase` , `Class`, `Assembly`, `Session` +`TestCase`, `Class`, `Assembly`, `Session` ### Fixture Dependencies @@ -110,4 +112,5 @@ Choose your path based on your needs: | Quick setup with TUnit | [Quick Start (TUnit)](getting-started/quick-start-tunit.md) | | Create your own fixture | [Creating Custom Fixtures](getting-started/creating-custom-fixtures.md) | | Combine fixtures | [Fixture Dependencies](getting-started/fixture-dependencies.md) | -| Explore builtin fixtures | [Fixture List](fixtures/list.md) | +| Explore built-in fixtures | [Fixture List](fixtures/list.md) | +| Learn how to simplify ASP.NET Core application testing | [Tutorial](tutorials/asp-net-core-application-testing.md) | diff --git a/docs/articles/tutorials/asp-net-core-application-testing.md b/docs/articles/tutorials/asp-net-core-application-testing.md index b77b70a..f563157 100644 --- a/docs/articles/tutorials/asp-net-core-application-testing.md +++ b/docs/articles/tutorials/asp-net-core-application-testing.md @@ -218,7 +218,7 @@ using Microsoft.Extensions.Time.Testing; using Newtonsoft.Json.Linq; using Xunit.v3; -public class WeatherForecastApiTests +public class ApiTests { protected FixtureSet FixtureSet { get; } = TestContext.Current.GetFeffFixture(); @@ -409,7 +409,6 @@ public class ApiTests } """); } - #endregion # region helpers @@ -461,7 +460,7 @@ public class ApiTests Execute the test from the command line: ```bash -dotnet test --filter "FullyQualifiedName~WeatherForecastApiTests" +dotnet test --filter "FullyQualifiedName~ApiTests" ``` The test should pass in a few seconds. Because all external factors (time, randomness, database name) are controlled by fixtures, the result is deterministic and repeatable. @@ -499,7 +498,7 @@ You have now written integration tests that: 2. **Request the application** via an HTTP client. 3. **Inject configuration** before startup via `IAppConfigurator`. 4. **Control externalities** like time and randomness with `FakeTimeFixture` and `FakeRandomFixture`. -5. **Isolate data** across tests using `TmpDatabaseNameFixture`. +5. **Isolate data** across tests using `TmpDatabaseNameFixture` and `DatabaseLifecycleFixture`. 6. **Assert on persistence** by combining HTTP calls with direct `DbContext` queries. ## See Also diff --git a/docs/docfx.json b/docs/docfx.json index e3bcc4f..b085be2 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -36,9 +36,15 @@ "default", "modern" ], + "markdownEngineProperties": { + "markdigExtensions": [ + "attributes" + ] + }, "globalMetadata": { "_appName": "FEFF.TestFixtures Library Documentation", "_appTitle": "FEFF.TestFixtures Library Documentation", + "_appLogoPath": "images/logo/logo-51-docfx.svg", "_enableSearch": true, "pdf": false } diff --git a/docs/images/logo/Concept.txt b/docs/images/logo/Concept.txt new file mode 100644 index 0000000..8e36825 --- /dev/null +++ b/docs/images/logo/Concept.txt @@ -0,0 +1,22 @@ + +The logo should represent one of these key ideas: +Interlocking / Reusable Blocks → "Fixtures" as modular components. + + +1. +Complete 2x2x2 cube structure (8 cubes total) +4 purple cubes + 4 teal cubes in alternating pattern +One of cubes is elevated (half-height raised) showing dynamic insertion +2-3 green check marks in one vertical column overlapping on the right side with Front-facing orientation (perpendicular to camera) +Professional 3D isometric view + +2. +The "Interlocking Fixture Blocks" +What it is: 3 simple, slightly overlapping rounded squares (like Lego bricks). Two are dark gray (setup), one is green (test runs). An arrow loops from the last block back to the first, indicating teardown/reset. +Why it fits: Mirrors MyFixtureSet(F1, F2) dependencies – fixtures assembling into larger fixtures. +Color: Dark slate (#2D333B) + Emerald green (#2DA44E – GitHub's success green). + +for github-repository's social media preview +convert docs/images/logo/logo.svg +to png with size 1280×640px +save as logo-1280-github.png \ No newline at end of file diff --git a/docs/images/logo/logo-128-nuget.png b/docs/images/logo/logo-128-nuget.png new file mode 100644 index 0000000..da8f463 Binary files /dev/null and b/docs/images/logo/logo-128-nuget.png differ diff --git a/docs/images/logo/logo-1280-github.png b/docs/images/logo/logo-1280-github.png new file mode 100644 index 0000000..71cd69a Binary files /dev/null and b/docs/images/logo/logo-1280-github.png differ diff --git a/docs/images/logo/logo-51-docfx.svg b/docs/images/logo/logo-51-docfx.svg new file mode 100644 index 0000000..353b10b --- /dev/null +++ b/docs/images/logo/logo-51-docfx.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/images/logo/logo.svg b/docs/images/logo/logo.svg new file mode 100644 index 0000000..9cafe01 --- /dev/null +++ b/docs/images/logo/logo.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c09ee98..129f771 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -24,7 +24,10 @@ Reusable testing components. https://github.com/metacoder-feff/FEFF.TestFixtures testing;fixtures;FEFF; + README.md + + logo-128-nuget.png true snupkg @@ -44,6 +47,9 @@ --> + + + diff --git a/src/FEFF.TestFixtures.AspNetCore.EF/DatabaseLifecycleFixture.cs b/src/FEFF.TestFixtures.AspNetCore.EF/DatabaseLifecycleFixture.cs index 55c8917..4efc019 100644 --- a/src/FEFF.TestFixtures.AspNetCore.EF/DatabaseLifecycleFixture.cs +++ b/src/FEFF.TestFixtures.AspNetCore.EF/DatabaseLifecycleFixture.cs @@ -9,14 +9,12 @@ namespace FEFF.TestFixtures.AspNetCore.EF; public interface IDatabaseLifecycleFixture { /// - /// Ensures that the database for the context exists and is created. + /// Gets the instance resolved from the service provider. /// - /// A token to cancel the operation. - /// A task representing the asynchronous operation. /// - /// Starts the application under test if not already running. + /// Accessing this property starts the application under test if not already running. /// - Task EnsureCreatedAsync(CancellationToken token); + DbContext LazyDbContext { get; } } /// @@ -29,7 +27,7 @@ public interface IDatabaseLifecycleFixture : IDatabaseLifecycleFixture /// /// Accessing this property starts the application under test if not already running. /// - TContext LazyDbContext { get; } + new TContext LazyDbContext { get; } } //TODO: skip options/properties + tests @@ -55,6 +53,8 @@ public sealed class DatabaseLifecycleFixture : IAsyncDisp /// public TContext LazyDbContext => _servicesFx.LazyServiceProvider.GetRequiredService(); + DbContext IDatabaseLifecycleFixture.LazyDbContext => LazyDbContext; + /// /// Creates a new database lifecycle management fixture. /// @@ -78,10 +78,24 @@ public async ValueTask DisposeAsync() // This can happen when database is locked or inaccessible // System.Console.Error.WriteLine($"[DatabaseLifecycleFixture] Warning: Failed to delete database: {ex.Message}"); } +} - /// - public async Task EnsureCreatedAsync(CancellationToken token) +/// +/// Provides extension methods for . +/// +public static class DatabaseLifecycleFixtureExtensions +{ + /// + /// Ensures that the database for the context exists and is created. + /// + /// The database lifecycle fixture. + /// A token to cancel the operation. + /// A task representing the asynchronous operation. + /// + /// Starts the application under test if not already running. + /// + public static async Task EnsureCreatedAsync(this IDatabaseLifecycleFixture src, CancellationToken token) { - await LazyDbContext.Database.EnsureCreatedAsync(token).ConfigureAwait(false); + await src.LazyDbContext.Database.EnsureCreatedAsync(token).ConfigureAwait(false); } } diff --git a/tests/Files/API/FEFF.TestFixtures.AspNetCore.EF.verified.txt b/tests/Files/API/FEFF.TestFixtures.AspNetCore.EF.verified.txt index e1dc71a..4fad105 100644 --- a/tests/Files/API/FEFF.TestFixtures.AspNetCore.EF.verified.txt +++ b/tests/Files/API/FEFF.TestFixtures.AspNetCore.EF.verified.txt @@ -2,6 +2,10 @@ [assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] namespace FEFF.TestFixtures.AspNetCore.EF { + public static class DatabaseLifecycleFixtureExtensions + { + public static System.Threading.Tasks.Task EnsureCreatedAsync(this FEFF.TestFixtures.AspNetCore.EF.IDatabaseLifecycleFixture src, System.Threading.CancellationToken token) { } + } [FEFF.TestFixtures.Fixture] public sealed class DatabaseLifecycleFixture : FEFF.TestFixtures.AspNetCore.EF.IDatabaseLifecycleFixture, FEFF.TestFixtures.AspNetCore.EF.IDatabaseLifecycleFixture, System.IAsyncDisposable where TEntryPoint : class @@ -10,11 +14,10 @@ namespace FEFF.TestFixtures.AspNetCore.EF public DatabaseLifecycleFixture(FEFF.TestFixtures.AspNetCore.AppManagerFixture app, FEFF.TestFixtures.AspNetCore.AppServicesFixture services) { } public TContext LazyDbContext { get; } public System.Threading.Tasks.ValueTask DisposeAsync() { } - public System.Threading.Tasks.Task EnsureCreatedAsync(System.Threading.CancellationToken token) { } } public interface IDatabaseLifecycleFixture { - System.Threading.Tasks.Task EnsureCreatedAsync(System.Threading.CancellationToken token); + Microsoft.EntityFrameworkCore.DbContext LazyDbContext { get; } } public interface IDatabaseLifecycleFixture : FEFF.TestFixtures.AspNetCore.EF.IDatabaseLifecycleFixture where TContext : Microsoft.EntityFrameworkCore.DbContext