diff --git a/.github/workflows/package.yml b/.github/workflows/build.yml similarity index 98% rename from .github/workflows/package.yml rename to .github/workflows/build.yml index cfa0d01..3fcba50 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Create NuGet Packages Workflow +name: CI Build on: push: diff --git a/.github/workflows/doc-pages.yml b/.github/workflows/doc-pages.yml new file mode 100644 index 0000000..fd51f2b --- /dev/null +++ b/.github/workflows/doc-pages.yml @@ -0,0 +1,46 @@ +name: Build and deploy github-pages Documentation site [docfx] +# Trigger the action on push to main + +on: + push: + branches: [ main ] +# Allows you to run this workflow manually from the Actions tab + # workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + actions: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Dotnet Setup + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - run: dotnet tool update -g docfx + - run: docfx docs/docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v5 + with: + # Upload entire repository + path: 'docs/_site' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f5b8bdc..c74d7a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,13 @@ jobs: ConnectionStrings__PgDb: "Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres" steps: + - name: Enable Automated Test Report Artifact Upload (FOR TUnit) + uses: actions/github-script@v9 + with: + script: | + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN']); + core.exportVariable('ACTIONS_RESULTS_URL', process.env['ACTIONS_RESULTS_URL']); + - name: Checkout uses: actions/checkout@v6 @@ -38,6 +45,9 @@ jobs: with: dotnet-version: '10.0.x' + - name: Install tools + run: dotnet tool install --global dotnet-reportgenerator-globaltool + - name: Restore dependencies run: dotnet restore --use-lock-file @@ -45,4 +55,12 @@ jobs: run: dotnet build --no-restore - name: Run tests - run: dotnet test --no-build + run: ./_DevOps/test-cov.sh + + - name: Upload JOB artifacts + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} # run this step even if previous step failed + with: + name: TestResults + path: ./TestResults + retention-days: 14 diff --git a/.gitignore b/.gitignore index 6800723..2baad1e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ tests/Files/API/*.received.* .nupkg/ nupkg/ +docs/_site/ +docs/api/ + ########################################################################### # Multiple sources ... # 1. FROM: gitlab .Net template diff --git a/README.md b/README.md index 51f96ae..c023896 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # FEFF.TestFixtures [![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) +[![Build](https://img.shields.io/github/actions/workflow/status/metacoder-feff/FEFF.TestFixtures/package.yml?label=Build)](https://github.com/metacoder-feff/FEFF.TestFixtures/actions/workflows/package.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) @@ -18,27 +20,7 @@ Fixture libraries: [Fixture list](#fixture-list) -## Terminology and Goals - -A **fixture** is a reusable component used for testing purposes. Fixtures can be packaged into libraries and reused by any number of testing projects. -The **fixture** is a class containing three optional parts: - -+ Setup code in the constructor; -+ State; -+ Teardown code in Dispose() or DisposeAsync(). - -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: - -| Scope name | Description | -| --- | -- | -| test-case | Fixtures are created and destroyed for each test case | -| class | Fixtures are created and destroyed once for each test class | -| collection | Fixtures are created and destroyed once for each [test collection](https://xunit.net/docs/running-tests-in-parallel#test-collections) | -| assembly | Fixtures are created and destroyed once for a test assembly | - -Every request for the same fixture within the same scope returns the same fixture instance. Therefore, class-, collection-, and assembly-level **fixtures can share state** between all tests within the same scope. +[Full Documentation](https://metacoder-feff.github.io/FEFF.TestFixtures/) ## Prerequisites @@ -133,14 +115,6 @@ public sealed class TmpDirectoryFixture : IDisposable } ``` -Where: - -| Fixture function | Implementation | -|--- | --- | -|Setup | Constructor | -|State | 'Path' property | -|Teardown | IDisposable | - ### Fixture Dependencies Fixtures can depend on other fixtures. Dependencies are injected via the constructor: @@ -175,24 +149,6 @@ Note: - All fixture dependencies (`MyCustomFixture1` and `MyCustomFixture2`) exist in the same scope as the dependent fixture (`MyFixtureSet` in the example above). - Fixtures cannot have cyclic dependencies. -## Advanced usage - -### Add/Get Fixture by Interface - -Documentation is currently under development. [See examples](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/d4e7561bd6bf0a3882e6f2777f0012c4ef9c3aa9/tests/FEFF.TestFixtures.Tests/Core/FixtureInterfaceTests.cs#L6). - -### Fixture Factory Internals - -Documentation is currently under development. [See examples](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/d4e7561bd6bf0a3882e6f2777f0012c4ef9c3aa9/src/FEFF.TestFixtures/Core/FixtureManager.cs#L14). - -### Advanced Fixture Registration - -Documentation is currently under development. [See examples](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/ecb983deb9af95dea222037e237e8fc08a4e9c1a/src/FEFF.TestFixtures/Fixtures/TmpDirectoryFixture.cs#L18). - -### Configuring Fixtures - -Documentation is currently under development. [See examples](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/ecb983deb9af95dea222037e237e8fc08a4e9c1a/src/FEFF.TestFixtures/Fixtures/TmpDirectoryFixture.cs#L38). - ## Fixture List ### FEFF.TestFixtures Library @@ -251,31 +207,4 @@ The following fixtures are planned but not yet implemented: + A unique S3 path prefix for test isolation + Fake outbound HTTP connection to stub integrations with third-party APIs -## Troubleshooting & FAQ - -### Which package should I use? - -+ For **xUnit v3** projects: Install `FEFF.TestFixtures.XunitV3` -+ For **TUnit** projects: Install `FEFF.TestFixtures.TUnit` -+ For **ASP.NET Core** integration tests: Also install `FEFF.TestFixtures.AspNetCore` - -### Why can't I resolve a fixture? - -Ensure you've added the assembly-level attribute (xUnit v3 only): - -```csharp -[assembly: FEFF.TestFixtures.Xunit.TestFixturesExtension] -``` - -### Can fixtures share state across tests? - -Yes. Class-, collection-, and assembly-scoped fixtures are singleton within their scope. All tests within that scope receive the same instance. - -### How do I clean up resources in a fixture? - -Implement `IDisposable` or `IAsyncDisposable` on your fixture class. The engine will call `Dispose()` or `DisposeAsync()` at the end of the fixture's scope. - -### Where do I report issues or ask questions? - -+ [GitHub Issues](https://github.com/metacoder-feff/FEFF.TestFixtures/issues) -+ [Discussions](https://github.com/metacoder-feff/FEFF.TestFixtures/discussions) +[Full Documentation](https://metacoder-feff.github.io/FEFF.TestFixtures/) diff --git a/_DevOps/test-cov.sh b/_DevOps/test-cov.sh new file mode 100755 index 0000000..22c39c1 --- /dev/null +++ b/_DevOps/test-cov.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -ex + +# dotnet tool install --global dotnet-reportgenerator-globaltool + +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +ROOT_DIR=$(realpath "${SCRIPT_DIR}/..") + +OUT_DIR=${ROOT_DIR}/TestResults +RAW_DIR=${OUT_DIR}/raw +COV_FILE=${OUT_DIR}/coverage/cobertura.xml +HTML_DIR=${OUT_DIR}/coverage/html + +rm -rf ${OUT_DIR}/* || echo ok + +dotnet test \ + --no-build \ + --solution "${ROOT_DIR}/FEFF.TestFixtures.slnx" \ + --results-directory "${RAW_DIR}" \ + --coverage \ + --coverage-output-format cobertura \ + --coverage-settings "${ROOT_DIR}/tests.runsettings" + +# merge all cobertura XMLs +# generate HTML +# generate Summary +reportgenerator \ + -reports:"${RAW_DIR}/*.cobertura.xml" \ + -targetdir:"${HTML_DIR}" \ + -reporttypes:"Cobertura;HtmlInline;TextSummary" + +mv "${HTML_DIR}/Cobertura.xml" "${COV_FILE}" +mv "${HTML_DIR}/Summary.txt" "${OUT_DIR}/" + +echo "See ${HTML_DIR}/index.html" + +# print total coverage to be captured by gitlab-ci +grep "Line coverage" "${OUT_DIR}/Summary.txt" diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..a645397 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1 @@ +# FEFF.TestFixtures API Reference diff --git a/docs/articles/01.getting-started/built-in-fixtures.md b/docs/articles/01.getting-started/built-in-fixtures.md new file mode 100644 index 0000000..c58da38 --- /dev/null +++ b/docs/articles/01.getting-started/built-in-fixtures.md @@ -0,0 +1,54 @@ +# Built-in Fixtures + +FEFF.TestFixtures provides a set of ready-to-use fixtures for common testing scenarios. These fixtures are managed by the framework and automatically cleaned up after use. + +## Core Fixtures (`FEFF.TestFixtures`) + +| Fixture | Description | +|---------|-------------| +| `TmpDirectoryFixture` | Creates a unique temp directory, auto-deletes after test | +| `EnvironmentFixture` | Snapshots process environment, restores after test | +| `TmpScopeIdFixture` | Generates unique ID per scope (for test isolation) | + +## ASP.NET Core Fixtures (`FEFF.TestFixtures.AspNetCore`) + +| Fixture | Description | +|---------|-------------| +| `AppManagerFixture` | Manages test host lifecycle for web apps | +| `AppClientFixture` | Provides `HttpClient` for API testing | +| `AppServicesFixture` | Access to application's service provider | +| `FakeTimeFixture` | Replaces `TimeProvider` with controllable fake | +| `FakeRandomFixture` | Replaces `Random` with deterministic fake | +| `FakeLoggerFixture` | Captures logs for assertions | +| `TmpDatabaseNameFixture` | Creates unique database name per test | + +## EF Core Integration (`FEFF.TestFixtures.AspNetCore.EF`) + +| Fixture | Description | +|---------|-------------| +| `DatabaseLifecycleFixture` | Creates/deletes database automatically | + +## Preview Fixtures +Fixtures that are implemented but not fully tested. + +### ASP.NET Core Fixtures (`FEFF.TestFixtures.AspNetCore`) + +| Fixture | Description | +|---------|-------------| +| `AuthorizedAppClientFixture` | Provides authenticated `HttpClient` with Bearer token for testing protected APIs | + + +### SignalR Integration (`FEFF.TestFixtures.AspNetCore.SignalR`) + +| Fixture | Description | +|---------|-------------| +| `SignalrClientFixture` | Creates and manages a SignalR test client connected to the application's hub; provides awaitable events for server-sent messages | + +## Planned +Fixtures that are being developed. + +| Fixture | Description | +|---------|-------------| +| `TmpRedisPrefixFixture` | Adds a unique Redis key prefix for test isolation | +| `TmpS3PrefixFixture` | Adds a unique S3 path prefix for test isolation | +| `MockHttpFixture` | Configures Fake outbound HTTP connection to stub integrations with third-party APIs (integrates [richardszalay/mockhttp](https://github.com/richardszalay/mockhttp) into testing pipeline) | diff --git a/docs/articles/01.getting-started/creating-custom-fixtures.md b/docs/articles/01.getting-started/creating-custom-fixtures.md new file mode 100644 index 0000000..b56b783 --- /dev/null +++ b/docs/articles/01.getting-started/creating-custom-fixtures.md @@ -0,0 +1,111 @@ +# Creating Custom Fixtures + +## Fixture Structure + +A fixture consists of three optional parts: + +| Part | Location | Purpose | +|------|----------|---------| +| **Setup** | Constructor | Initialize resources | +| **State** | Properties/Fields | Expose data to tests | +| **Teardown** | `Dispose()` / `DisposeAsync()` | Clean up resources | + +## Example: Building TmpDirectoryFixture + +Let's create a fixture that provides a unique temporary directory for each test scope and automatically cleans it up afterward. + +### Step 1: Basic Structure + +Start with the minimal fixture structure: + +```csharp +using FEFF.TestFixtures; + +[Fixture] +public class TmpDirectoryFixture +{ + public string Path { get; } + + public TmpDirectoryFixture() + { + Path = Directory.CreateTempSubdirectory().FullName; + } +} +``` + +**Key elements:** +- `[Fixture]` attribute - Marks the class for discovery by the framework +- `Path` property - Exposes the directory path to tests +- Constructor - Creates the temporary directory (Setup) + +### Step 2: Add Teardown + +Implement `IDisposable` to clean up the directory: + +```csharp +[Fixture] +public class TmpDirectoryFixture : IDisposable +{ + public string Path { get; } + + public TmpDirectoryFixture() + { + Path = Directory.CreateTempSubdirectory().FullName; + } + + public void Dispose() + { + Directory.Delete(Path, true); + } +} +``` + +> **Note:** The framework also supports `IAsyncDisposable` for fixtures requiring asynchronous cleanup. Implement `DisposeAsync()` instead of `Dispose()` when your teardown logic involves async operations (e.g., database connections, network calls, file I/O). + +```csharp + public async ValueTask DisposeAsync() + { + // Async cleanup logic here + await Task.Delay(10); // Example: upload logs before deletion + Directory.Delete(Path, true); + } +``` + +### Step 3: Add Error Handling + +Protect against disposal errors: + +```csharp +[Fixture] +public class TmpDirectoryFixture : IDisposable +{ + public string Path { get; } + + public TmpDirectoryFixture() + { + Path = Directory.CreateTempSubdirectory().FullName; + } + + public void Dispose() + { + try + { + Directory.Delete(Path, true); + } + catch (DirectoryNotFoundException) + { + // Directory already deleted - ignore + } + } +} +``` + +## Reference: TmpDirectoryFixture Source and Tests + +For a complete working example, see the production implementation and tests: + +| Resource | Description | +|----------|-------------| +| [Source Code](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures/Fixtures/TmpDirectoryFixture.cs) | Full implementation of `TmpDirectoryFixture` with options support | +| [Unit Tests](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/FixturesTests/FEFF.TestFixtures/TmpDirectoryFixtureTests.cs) | Comprehensive test coverage | +| [Options Tests](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/FixturesTests/FEFF.TestFixtures/TmpDirectoryFixtureOptionsTests.cs) | Configuration and options usage examples | \ No newline at end of file diff --git a/docs/articles/01.getting-started/fixture-dependencies.md b/docs/articles/01.getting-started/fixture-dependencies.md new file mode 100644 index 0000000..6e1e613 --- /dev/null +++ b/docs/articles/01.getting-started/fixture-dependencies.md @@ -0,0 +1,149 @@ +# Fixture Dependencies + +Fixtures can depend on other fixtures through constructor injection. This allows you to compose complex test setups from simpler, reusable components. + +## Basic Concept + +When a fixture has other fixtures as constructor parameters, the framework automatically resolves and injects them. All fixture dependencies share the same scope as the dependent fixture. + +## Example: Simple Dependency + +Let's create a fixture that depends on another fixture: + +### Step 1: Create a Base Fixture + +First, create a simple fixture that provides a base resource: + +```csharp +using FEFF.TestFixtures; + +[Fixture] +public class TmpDirectoryFixture +{ + public string Path { get; } + + public TmpDirectoryFixture() + { + Path = Directory.CreateTempSubdirectory().FullName; + } +} +``` + +### Step 2: Create a Dependent Fixture + +Now create a fixture that depends on `TmpDirectoryFixture`: + +```csharp +using FEFF.TestFixtures; + +[Fixture] +public class LogFileFixture +{ + public string LogFilePath { get; } + + public LogFileFixture(TmpDirectoryFixture tmpDir) + { + LogFilePath = Path.Combine(tmpDir.Path, "app.log"); + File.WriteAllText(LogFilePath, string.Empty); + } +} +``` + +### Step 3: Use in Tests + +```csharp +using FEFF.TestFixtures; + +public class LogFileTests +{ + protected LogFileFixture LogFile { get; } = + TestContext.Current.GetFeffFixture(); + + [Fact] + public void LogFile__should_be_created() + { + // Arrange + File.Exists(LogFile.LogFilePath).Should().BeFalse(); + + // Act + File.WriteAllText(LogFile.LogFilePath, "test log entry"); + + // Assert + File.Exists(LogFile.LogFilePath) + .Should().BeTrue(); + + File.ReadAllText(LogFile.LogFilePath) + .Should().Be("test log entry"); + } +} +``` + +## Multiple Dependencies + +Fixtures can depend on multiple other fixtures: + +```csharp +[Fixture] +public class DatabaseTestFixture +{ + public string ConnectionString { get; } + + public DatabaseTestFixture( + TmpDirectoryFixture tmpDir, + EnvironmentFixture env) + { + var dbPath = Path.Combine(tmpDir.Path, "test.db"); + ConnectionString = $"Data Source={dbPath}"; + } +} +``` + +## Record-Based Fixtures + +For cleaner syntax, you can use C# records: + +```csharp +[Fixture] +public record TestEnvironmentFixture( + TmpDirectoryFixture TempDir, + EnvironmentFixture Environment, + TmpScopeIdFixture TstId +); + +public class ComplexTests +{ + protected TestEnvironmentFixture Env { get; } = + TestContext.Current.GetFeffFixture(); + + [Fact] + public void Test__example() + { + // Access dependencies directly + var tempPath = Env.TempDir.Path; + var randomValue = Env.TstId.Value; + // ... + } +} +``` + +## Scope Inheritance + +**Important:** All fixture dependencies exist in the **same scope** as the dependent fixture. + +```csharp +public class SharedStateTests +{ + // Both fixtures share the same Class scope + protected LogFileFixture LogFile { get; } = + TestContext.Current.GetFeffFixture(FixtureScopeType.Class); + + // The TmpDirectoryFixture inside LogFileFixture also uses Class scope + // Even though you didn't specify it explicitly +} +``` + +### Scope Rules + +1. **Same Scope**: Dependencies are created in the same scope as the parent fixture +2. **No Cyclic Dependencies**: Fixtures cannot depend on each other cyclically (A → B → A) +3. **Lazy Initialization**: Fixtures are created only when first requested diff --git a/docs/articles/01.getting-started/quick-start-tunit.md b/docs/articles/01.getting-started/quick-start-tunit.md new file mode 100644 index 0000000..eb85789 --- /dev/null +++ b/docs/articles/01.getting-started/quick-start-tunit.md @@ -0,0 +1,94 @@ +# Quick Start Guide (TUnit) + +This guide walks you through setting up and using FEFF.TestFixtures with TUnit. + +## Prerequisites + +- .NET 8.0 or later +- TUnit test framework +- Basic knowledge of TUnit testing + +## Installation + +Add the TUnit integration package to your test project: + +```bash +dotnet add package FEFF.TestFixtures.TUnit +``` +## Configuration + +No assembly-level configuration is required for TUnit. The integration works automatically once the package is installed. + +## Your First Test + +Here's a complete example using the built-in `TmpDirectoryFixture`: + +```csharp +using AwesomeAssertions; +using FEFF.TestFixtures; +using FEFF.TestFixtures.TUnit; + +namespace MyProject.Tests; + +public class SystemUnderTest +{ + public static void Write(string filePath, string content) => + File.WriteAllText(filePath, content, Encoding.UTF8); +} + +public class FileTests +{ + // Get the fixture instance from the test context + protected TmpDirectoryFixture TmpDir { get; } = + TestContext.Current!.GetFeffFixture(); + + [Test] + public async Task File__should_be_created() + { + // Arrange + var filePath = TmpDir.Path + "/file.tmp"; + File.Exists(filePath).Should().BeFalse(); + + // Act + SystemUnderTest.Write(filePath, "some-data"); + + // Assert + File.Exists(filePath).Should().BeTrue(); + File.ReadAllText(filePath, Encoding.UTF8).Should().Be("some-data"); + } +} +``` + +### How It Works + +1. **Fixture Declaration**: The `TmpDir` property retrieves a `TmpDirectoryFixture` instance from the test context. +2. **Automatic Lifecycle**: The fixture is created on first use (lazy initialization) and automatically disposed after the test completes. +3. **Test Case Scope**: By default, fixtures use the `test-case` scope, meaning a new instance is created for each test method. +4. **Cleanup**: The temporary directory and all its contents are automatically deleted after the test. + +## Fixture Scopes + +FEFF.TestFixtures supports five scope types for TUnit: + +| Scope | Description | Use Case | +|-------|-------------|----------| +| `TestCase` | New fixture for each test method | Default, isolated tests | +| `Class` | One fixture per test class | Share state across tests in a class | +| `Assembly` | One fixture per test assembly | Run once for all tests in the assembly | +| `Session` | One fixture per test session | Run once for all tests | + +### Changing the Scope + +Use the overloaded method to specify a different scope: + +```csharp +protected MyFixture MyFixture { get; } = + TestContext.Current!.GetFeffFixture(FixtureScopeType.Class); +``` + +## Examples + +Full working examples are available in the [GitHub repository](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples): + +- [TUnit Examples](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples/ExampleTests.TUnit) +- [ASP.NET Core Examples](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples/ExampleTests.AspNetCore) diff --git a/docs/articles/01.getting-started/quick-start-xunit.md b/docs/articles/01.getting-started/quick-start-xunit.md new file mode 100644 index 0000000..4ffa3c3 --- /dev/null +++ b/docs/articles/01.getting-started/quick-start-xunit.md @@ -0,0 +1,104 @@ +# Quick Start Guide (xUnit v3) + +This guide walks you through setting up and using FEFF.TestFixtures with xUnit v3. + +## Prerequisites + +- .NET 8.0 or later +- xUnit v3 test framework +- Basic knowledge of xUnit testing + +## Installation + +Add the xUnit v3 integration package to your test project: + +```bash +dotnet add package FEFF.TestFixtures.XunitV3 +``` + +## Initilize extension + +Add the assembly-level attribute to initialize the test fixtures extension in any file in your test project: + +```csharp +[assembly: FEFF.TestFixtures.Xunit.TestFixturesExtension] +``` + +## Your First Test + +Here's a complete example using the built-in `TmpDirectoryFixture`: + +```csharp +using AwesomeAssertions; +using FEFF.TestFixtures; +using Xunit.v3; + +[assembly: FEFF.TestFixtures.Xunit.TestFixturesExtension] + +namespace MyProject.Tests; + +public class SystemUnderTest +{ + public static void Write(string filePath, string content) => + File.WriteAllText(filePath, content, Encoding.UTF8); +} + +public class FileTests +{ + // Get the fixture instance from the test context + protected TmpDirectoryFixture TmpDir { get; } = + TestContext.Current.GetFeffFixture(); + + [Fact] + public void File__should_be_created() + { + // Arrange + var filePath = TmpDir.Path + "/file.tmp"; + File.Exists(filePath) + .Should().BeFalse(); + + // Act + SystemUnderTest.Write(filePath, "some-data"); + + // Assert + File.Exists(filePath) + .Should().BeTrue(); + File.ReadAllText(filePath, Encoding.UTF8) + .Should().Be("some-data"); + } +} +``` + +### How It Works + +1. **Fixture Declaration**: The `TmpDir` property retrieves a `TmpDirectoryFixture` instance from the test context. +2. **Automatic Lifecycle**: The fixture is created on first use (lazy initialization) and automatically disposed after the test completes. +3. **Test Case Scope**: By default, fixtures use the `test-case` scope, meaning a new instance is created for each test method. +4. **Cleanup**: The temporary directory and all its contents are automatically deleted after the test. + +## Fixture Scopes + +FEFF.TestFixtures supports four scope types: + +| Scope | Description | Use Case | +|-------|-------------|----------| +| `TestCase` | New fixture for each test case | Default, isolated tests | +| `Class` | One fixture per test class | Share state across tests in a class | +| `Collection` | One fixture per test collection | Share state across test collections | +| `Assembly` | One fixture per test assembly | Run once for all tests in the assembly | + +### Changing the Scope + +Use the overloaded method to specify a different scope: + +```csharp +protected MyFixture MyFixture { get; } = + TestContext.Current.GetFeffFixture(FixtureScopeType.Class); +``` + +## Examples + +Full working examples are available in the [GitHub repository](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples): + +- [xUnit v3 Examples](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples/ExampleTests.XunitV3) +- [ASP.NET Core Examples](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples/ExampleTests.AspNetCore) diff --git a/docs/articles/01.getting-started/toc.yml b/docs/articles/01.getting-started/toc.yml new file mode 100644 index 0000000..19cb14e --- /dev/null +++ b/docs/articles/01.getting-started/toc.yml @@ -0,0 +1,10 @@ +- name: Quick Start (xUnit v3) + href: quick-start-xunit.md +- name: Quick Start (TUnit) + href: quick-start-tunit.md +- name: Creating Custom Fixtures + href: creating-custom-fixtures.md +- name: Fixture Dependencies + href: fixture-dependencies.md +- name: Built-in Fixtures + href: built-in-fixtures.md \ No newline at end of file diff --git a/docs/articles/02.advanced/add-get-fixture-by-interface.md b/docs/articles/02.advanced/add-get-fixture-by-interface.md new file mode 100644 index 0000000..4e131a2 --- /dev/null +++ b/docs/articles/02.advanced/add-get-fixture-by-interface.md @@ -0,0 +1,62 @@ +# Add/Get Fixture by Interface + +By default, fixtures are registered and retrieved by their concrete implementation type. However, FEFF.TestFixtures supports registering fixtures by interface, enabling more flexible and testable designs. + +## Registering Fixture by Interface + +To register a fixture by interface, use the `RegisterWithType` parameter in the `[Fixture]` attribute: + +```csharp +internal interface IMyFixture +{ + string Value { get; } +} + +[Fixture(RegisterWithType = typeof(IMyFixture))] +internal class MyFixture : IMyFixture +{ + public string Value { get; } = "Hello"; +} +``` + +With this configuration, the fixture can be retrieved using either the implementation type or the interface type. + +## Retrieving Fixtures + +When a fixture is registered with `RegisterWithType`, it becomes accessible through both types: + +```csharp +// Retrieve by implementation type +MyFixture fixture1 = Helper.GetFixture(); + +// Retrieve by interface type +IMyFixture fixture2 = Helper.GetFixture(); + +// Both references point to the same instance +Assert.Same(fixture1, fixture2); +``` + +This dual registration enables: + +- **Loose coupling**: Tests and other fixtures can depend on interfaces rather than concrete implementations +- **Better abstraction**: Fixture implementation details are hidden behind interfaces + +> [!TIP] +> The type specified in `RegisterWithType` must be an interface or base class that the fixture implementation actually implements. Attempting to register with an incompatible type will result in an `InvalidCastException` at **testing time**. + +## Multiple Interface Registrations + +A single fixture can be registered with multiple interfaces by combining the attribute: + +```csharp +[Fixture(RegisterWithType = typeof(IFixtureA))] +[Fixture(RegisterWithType = typeof(IFixtureB))] +internal class MultiInterfaceFixture : IFixtureA, IFixtureB +{ + // Implementation +} +``` + +## See Also + +- [FixtureInterfaceTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/EngineTests/FixtureInterfaceTests.cs) diff --git a/docs/articles/02.advanced/advanced-fixture-registration.md b/docs/articles/02.advanced/advanced-fixture-registration.md new file mode 100644 index 0000000..be7b19a --- /dev/null +++ b/docs/articles/02.advanced/advanced-fixture-registration.md @@ -0,0 +1,71 @@ +# Advanced Fixture Registration + +While the `[Fixture]` attribute provides a simple way to discover and register fixtures, FEFF.TestFixtures offers more advanced registration patterns for fixtures that require configuration or custom dependency injection setup. + +## Two Major Registration Approaches + +There are two primary ways to register fixtures with the FEFF.TestFixtures engine: + +### 1. Attribute-Based Discovery (Simple) + +The simplest approach uses the `[Fixture]` attribute on your fixture class. The engine automatically discovers and registers these fixtures: + +```csharp +[Fixture] +public class MySimpleFixture +{ + public MySimpleFixture() + { + // Constructor-based initialization + } +} +``` + +This approach works well for fixtures that don't require external configuration. + +### 2. Advanced Registration with IFixtureRegistrar + +For fixtures that need configuration options or custom DI setup, implement the `IFixtureRegistrar` interface on any public class and provide a static `RegisterFixture(IServiceCollection services)` method: + +```csharp +public class MyFixtureRegistrar : IFixtureRegistrar +{ + public static void RegisterFixture(IServiceCollection services) + { + // Custom registration logic + services.Configure(options => + { + options.SomeSetting = "value"; + }); + } +} +``` + +This approach gives you full control over how the fixture and its dependencies are registered in the DI container. + +> [!TIP] +> The `DiscoveryService` searches for any public class that implements `IFixtureRegistrar`. + +## Configuring Fixtures + +Fixtures that implement `IFixtureRegistrar` can expose configuration options through the `Microsoft.Extensions.Options` pattern. See the dedicated article for a complete walkthrough: + +👉 [Configuring Fixtures](configuring-fixtures.md) - Learn how to add configuration support to fixtures like `TmpDirectoryFixture`. + +## When to Use Advanced Registration + +Use advanced registration when you need to: + +- **Provide configuration options** - Allow users to customize fixture behavior +- **Register additional services** - Register supporting services alongside the fixture +- **Custom lifetime management** - Control how and when the fixture is disposed +- **Integration with external systems** - Configure external dependencies before fixture creation + +## See Also + +| Resource | Description | +|----------|-------------| +| [IFixtureRegistrar.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Abstractions/Registration/IFixtureRegistrar.cs) | Interface definition for custom registration | +| [FixtureAttribute.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Abstractions/Registration/FixtureAttribute.cs) | Attribute for fixture discovery | +| [AssemblyDiscoveryService.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/AssemblyDiscoveryService.cs) | Service for discovering fixtures via reflection | +| [ReflectiveFixtureCollector.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/ReflectiveFixtureCollector.cs) | Collector for fixture types from assemblies | diff --git a/docs/articles/02.advanced/configuring-fixtures.md b/docs/articles/02.advanced/configuring-fixtures.md new file mode 100644 index 0000000..9e6b5f6 --- /dev/null +++ b/docs/articles/02.advanced/configuring-fixtures.md @@ -0,0 +1,164 @@ +# Configuring Fixtures + +Fixtures that implement `IFixtureRegistrar` can expose configuration options through the standard `Microsoft.Extensions.Options` pattern. This article demonstrates how to add configuration support to a fixture using `TmpDirectoryFixture` as an example. + +## Overview + +When you need to customize fixture behavior without modifying test code, configure the fixture through **Environment variables**. + +## Example: TmpDirectoryFixture Configuration + +The `TmpDirectoryFixture` provides a unique temporary directory for each test scope. By implementing configuration options, users can control: + +- Whether the directory is deleted after disposal (useful for debugging or CI optimization) +- A custom prefix for the directory name (for easier identification in temp folders) + +### Step 1: Define Configuration Options + +Create an `Options` class and any supporting enums: + +```csharp + /// + /// Specifies the behavior when the fixture is disposed. + /// + public enum DisposeType + { + /// + /// Deletes the temporary directory and its contents on disposal. + /// + Delete, + + /// + /// Skips deletion of the temporary directory on disposal. + /// + /// + /// Can be used for optimization in CI environments. + /// + Skip + } + + /// + /// Configuration options for TmpDirectoryFixture. + /// + public class Options + { + /// + /// Gets or sets whether the temporary directory should be deleted on disposal. + /// Defaults to DisposeType.Delete. + /// + public DisposeType DisposeType { get; set; } = DisposeType.Delete; + + /// + /// Gets or sets the prefix for the temporary directory name. + /// + public string? Prefix { get; set; } + } +``` + +### Step 2: Register Options with DI + +Implement the `RegisterFixture` method to register the options: + +> [!TIP] +> The `IFixtureRegistrar` can be implemented on any public type including a fixture class. + +```csharp +[Fixture] +public sealed class TmpDirectoryFixture : IFixtureRegistrar +{ + /// + /// Registers TmpDirectoryFixture configuration options with the service collection, + /// binding from the TmpDirectoryFixture configuration section. + /// + public static void RegisterFixture(IServiceCollection services) + { + services + .AddOptions() + .BindConfiguration(nameof(TmpDirectoryFixture)) + ; + } +} +``` + +This enables: +- Binding from configuration sections (e.g., environment variables with `__` separator) +- Type-safe access via `IOptions` + +### Step 3: Inject and Use Options + +Inject `IOptions` into the fixture constructor: + +```csharp + private readonly Options _opts; + public string Path { get; } + + public TmpDirectoryFixture(IOptions opts) + { + _opts = opts.Value; + Path = Directory.CreateTempSubdirectory(_opts.Prefix).FullName; + } + + public void Dispose() + { + if (_opts.DisposeType != DisposeType.Delete) + return; + + try + { + Directory.Delete(Path, true); + } + catch (DirectoryNotFoundException) + { + } + } +} +``` + +## Configuration Methods + +### Environment Variables + +Use environment variables with the `__` (double underscore) separator for hierarchy: + +```bash +# Windows +set TmpDirectoryFixture__DisposeType=Skip +set TmpDirectoryFixture__Prefix=mytest_ + +# Linux/macOS +export TmpDirectoryFixture__DisposeType=Skip +export TmpDirectoryFixture__Prefix=mytest_ +``` + +### Programmatic Configuration + +Configure options directly in code during fixture manager setup: + +```csharp +var manager = new FixtureManagerBuilder() + .ConfigureServices(services => + { + services.Configure(options => + { + options.DisposeType = TmpDirectoryFixture.DisposeType.Skip; + options.Prefix = "custom_"; + }); + }) + .Build(); +``` + +## Best Practices + +1. **Provide sensible defaults** - Always set default values in the `Options` class properties +2. **Use clear naming** - Prefix option properties with the fixture name in configuration +3. **Document all options** - Include XML documentation comments for IntelliSense support +4. **Validate configuration** - Consider adding validation logic in the fixture constructor +5. **Make options optional** - Allow fixtures to work without explicit configuration + +## See Also + +| Resource | Description | +|----------|-------------| +| [TmpDirectoryFixture.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures/Fixtures/TmpDirectoryFixture.cs) | Complete source code example | +| [TmpDirectoryFixtureTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/Fixtures/TmpDirectoryFixtureTests.cs) | Unit tests for TmpDirectoryFixture | +| [OptionsConfigurationTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/EngineTests/OptionsConfigurationTests.cs) | Tests for options configuration patterns | diff --git a/docs/articles/02.advanced/fixture-factory-internals.md b/docs/articles/02.advanced/fixture-factory-internals.md new file mode 100644 index 0000000..4b21fbd --- /dev/null +++ b/docs/articles/02.advanced/fixture-factory-internals.md @@ -0,0 +1,154 @@ +# Fixture Factory Internals + +The `FixtureManager` is the core component responsible for creating, caching, and managing the lifecycle of fixture scopes. It acts as a factory and coordinator for all fixture instances within the test execution pipeline. + +## Core Components + +### FixtureManager + +The central manager class that orchestrates fixture lifecycle: + +```csharp +public sealed class FixtureManager : IAsyncDisposable +{ + private readonly ServiceProvider _provider; + private readonly Dictionary _scopes; + private readonly object _lock; +} +``` + +**Key Responsibilities:** +- Creates new `FixtureScope` instances on demand +- Caches scopes in a thread-safe dictionary +- Provides `GetScope(string id)` for retrieving or creating scopes +- Manages global disposal through `DisposeAsync()` +- Supports individual scope removal via `RemoveScopeAsync(string scopeId)` + +### FixtureScope + +Represents an isolated fixture container with its own DI scope: + +```csharp +internal sealed class FixtureScope : IAsyncDisposable, IFixtureScope +{ + private readonly AsyncServiceScope _serviceScope; + + public T GetFixture() where T : notnull; + public ValueTask DisposeAsync(); +} +``` + +**Key Responsibilities:** +- Wraps `AsyncServiceScope` from Microsoft.Extensions.DependencyInjection +- Resolves fixtures through dependency injection +- Handles async disposal of scoped services + +## Usage Patterns + +### Creating a Scope + +Scopes are created lazily when first requested: + +```csharp +var fixtureManager = new FixtureManagerBuilder() + .Build(); + +// First call creates the scope +IFixtureScope scope1 = fixtureManager.GetScope("test-case-1"); + +// Second call returns cached scope +IFixtureScope scope2 = fixtureManager.GetScope("test-case-1"); + +// scope1 and scope2 reference the same instance +``` + +### Individual Scope Removal + +Removes and disposes a specific scope: + +```csharp +await fixtureManager.RemoveScopeAsync("test-case-1"); +``` + +The scope is removed from the cache and disposed asynchronously. If the scope doesn't exist, the operation completes silently. + + +### Global Disposal + +Disposes all scopes and the service provider: + +```csharp +await fixtureManager.DisposeAsync(); +``` + +This iterates through all cached scopes and disposes them in order, followed by the root `ServiceProvider`. + +## Lifecycle Management + +### Fixture Registration and Discovery + +Before scopes can be created, fixtures must be discovered and registered with the DI container. + +**Discovery Process:** + +1. Framework scans assemblies for types marked with `[Fixture]` or types that implement `IFixtureRegistrar` +2. Registers fixtures in the `ServiceCollection` +3. Builds the root `ServiceProvider` for scope creation +4. `ServiceProvider` is used by `FixtureManager` to resolve fixures + +### Scope Creation Flow + +1. User calls `GetScope(id)` +2. Manager checks if scope exists (thread-safe) +3. If not exists, creates new `FixtureScope` with DI container +4. Caches scope by identifier +5. Returns scope instance + +### Scope Disposal Flow + +1. User calls `DisposeAsync()` or `RemoveScopeAsync(id)` +2. Manager collects all disposables +3. Disposes fixtures in reverse registration order +4. Disposes underlying `ServiceProvider` +5. Marks manager as disposed (prevents further scope creation) + +## Thread Safety + +`FixtureManager` is designed for concurrent access: + +- Uses `Lock` (NET9.0+) or `object` (legacy) for synchronization +- Double-check pattern prevents unnecessary lock acquisition +- Disposal flag prevents operations on disposed manager +- Each `FixtureScope` is isolated and thread-confined + +## Dependency Injection Integration + +The fixture factory leverages `Microsoft.Extensions.DependencyInjection`: + +- Root `ServiceProvider` is created from configured services +- Each scope creates child `AsyncServiceScope` +- Fixtures are created within one of scopes +- Fixtures are resolved via `AsyncServiceScope.ServiceProvider.GetRequiredService()` +- Fixtures are disposed on scope cleanup + +## Error Handling + +- **Null/Empty IDs**: Throws `ArgumentException` +- **Disposed Manager**: Throws `ObjectDisposedException` +- **Missing Fixtures**: Throws from DI container (`InvalidOperationException`) +- **Concurrent Access**: Safely handled via locking + +## See Also + +### Sources + +- [FixtureManager.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/FixtureManager.cs) +- [FixtureScope.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/FixtureScope.cs) +- [FixtureManagerBuilder.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/FixtureManagerBuilder.cs) +- [AssemblyDiscoveryService.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.Engine/Engine/AssemblyDiscoveryService.cs) + +### Tests + +- [FixtureManagerTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/EngineTests/FixtureManagerTests.cs) +- [FixtureScopeTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/EngineTests/FixtureScopeTests.cs) +- [ConcurrentAccessTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/EngineTests/ConcurrentAccessTests.cs) diff --git a/docs/articles/02.advanced/fixture-parameterization.md b/docs/articles/02.advanced/fixture-parameterization.md new file mode 100644 index 0000000..3863353 --- /dev/null +++ b/docs/articles/02.advanced/fixture-parameterization.md @@ -0,0 +1,102 @@ +# Fixture Parameterization + +Fixture parameterization allows you to create fixture instances with concrete argument values by defining options types that supply configuration data. This pattern is essential when you need the same fixture to behave differently across tests based on custom parameters. + +## Overview + +While standard fixtures are created with default behavior, parameterized fixtures accept configuration through a dedicated options type. This options type is itself a fixture, enabling dependency injection and test isolation. + +The pattern consists of three components: + +1. **Options Interface** - Defines the contract for configuration data +2. **Options Fixture** - Implements the interface with concrete values +3. **Parameterized Fixture** - Consumes the options fixture via constructor injection of generic type parameter + +## Example: TmpDatabaseNameFixture + +The `TmpDatabaseNameFixture` demonstrates this pattern by allowing tests to specify which connection strings should be redirected to temporary databases. + +### Step 1: Define the Options Interface + +```csharp +public interface ITmpDatabaseNameFixtureOptions +{ + IReadOnlyCollection ConnectionStringNames { get; } +} +``` + +This interface specifies what configuration data the parameterized fixture needs. + +### Step 2: Create a Parameterized Fixture + +The parameterized fixture is a generic class that accepts the options type as a type parameter: + +```csharp +[Fixture] +public class TmpDatabaseNameFixture +where TOptionsFixture : ITmpDatabaseNameFixtureOptions +{ + public TmpDatabaseNameFixture( + AppManagerFixture app, + TOptionsFixture opts) + { + app.ConfigurationBuilder.UseDatabaseNamePostfix( + $"test-{Guid.New()}", + opts.ConnectionStringNames); + } +} +``` + +Key characteristics: +- **Generic type parameter** `TOptionsFixture` - The options fixture type +- **Constraint** `where TOptionsFixture : ITmpDatabaseNameFixtureOptions` - Ensures the options type implements the required interface +- **Constructor injection** - Receives the options fixture via DI alongside other dependencies + +### Step 3: Create an Options Fixture + +Create a fixture that implements the options interface: + +```csharp +[Fixture] +public class OptionsFixture : ITmpDatabaseNameFixtureOptions +{ + public IReadOnlyCollection ConnectionStringNames => + ["DefaultConnection"]; +} +``` + +The options fixture can: +- Return hardcoded values +- Read from environment variables +- Depend on other fixtures for dynamic configuration + +### Step 4: Use the Parameterized Fixture + +Create the fuxture instance: + +```csharp +var fixtureInstance = TestContext.Current.GetFeffFixture>(); +//... +``` + +The generic type parameter `OptionsFixture` tells `TmpDatabaseNameFixture` which options to use. + +> [!TIP] +> Single `OptionsFixture` class can implement multiple different interfaces to parameterize multiple fixtures used in a test suite. + +## Benefits of Fixture Parameterization + +- **Type Safety** - Options are compile-time checked via generics +- **Composability** - Options fixtures can depend on other fixtures +- **Reusability** - Same parameterized fixture works with different options +- **Clarity** - Options type documents required configuration + +## See Also + +| Resource | Description | +|----------|-------------| +| [TmpDatabaseNameFixture.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.AspNetCore/Fixtures/TmpDatabaseNameFixture.cs) | Parameterized fixture implementation | +| [ITmpDatabaseNameFixtureOptions.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/src/FEFF.TestFixtures.AspNetCore/Fixtures/TmpDatabaseNameFixture.cs) | Options interface definition | +| [ApiTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/examples/ExampleTests.AspNetCore/ApiTests.cs) | Example usage in API tests | +| [TmpDatabaseNameFixtureTests.cs](https://github.com/metacoder-feff/FEFF.TestFixtures/blob/main/tests/FEFF.TestFixtures.Tests/FixturesTests/FEFF.TestFixtures.AspNetCore/TmpDatabaseNameFixtureTests.cs) | Unit tests for parameterization | +| [Configuring Fixtures](configuring-fixtures.md) | Alternative configuration via environment variables | diff --git a/docs/articles/02.advanced/toc.yml b/docs/articles/02.advanced/toc.yml new file mode 100644 index 0000000..359a1a3 --- /dev/null +++ b/docs/articles/02.advanced/toc.yml @@ -0,0 +1,10 @@ +- name: Add/Get Fixture by Interface + href: add-get-fixture-by-interface.md +- name: Fixture Factory Internals + href: fixture-factory-internals.md +- name: Advanced Fixture Registration + href: advanced-fixture-registration.md +- name: Configuring Fixtures + href: configuring-fixtures.md +- name: Fixture Parameterization + href: fixture-parameterization.md diff --git a/docs/articles/03.fixtures/toc.yml b/docs/articles/03.fixtures/toc.yml new file mode 100644 index 0000000..99a3931 --- /dev/null +++ b/docs/articles/03.fixtures/toc.yml @@ -0,0 +1,2 @@ +- name: stub + href: stub.md \ No newline at end of file diff --git a/docs/articles/overview.md b/docs/articles/overview.md new file mode 100644 index 0000000..fc706ba --- /dev/null +++ b/docs/articles/overview.md @@ -0,0 +1,113 @@ +# 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. + +## What is a Fixture? + +A **fixture** is a reusable component that manages resources for testing purposes. Each fixture has three parts: + +| Part | Purpose | Example | +|------|---------|---------| +| **Setup** | Initialize resources | Create a temp directory, open a database connection | +| **State** | Expose data to tests | File path, connection string, test data | +| **Teardown** | Clean up resources | Delete files, close connections, rollback transactions | + +## Goals + +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 + +Additionaly: +✅ **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. + +```csharp +[Fixture] +public class MyFixture : IDisposable +{ + public string Resource { get; } + + public MyFixture() + { + Resource = "initialized"; + } + + public void Dispose() + { + // Cleanup + } +} +``` + +### 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 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` + +### Fixture Dependencies + +Fixtures can depend on other fixtures through constructor injection: + +```csharp +[Fixture] +public class ComplexFixture +{ + public ComplexFixture(SimpleFixture1 f1, SimpleFixture2 f2) + { + // Dependencies automatically resolved + } +} +``` + +All dependencies share the same scope as the parent fixture. + +## Package Structure + +The library is distributed as multiple NuGet packages: + +| Package | Purpose | +|---------|---------| +| `FEFF.TestFixtures.XunitV3` | Integration with xUnit v3 | +| `FEFF.TestFixtures.TUnit` | Integration with TUnit | +| `FEFF.TestFixtures` | Core fixtures (temp directory, environment, etc.) | +| `FEFF.TestFixtures.AspNetCore` | ASP.NET Core testing fixtures | +| `FEFF.TestFixtures.AspNetCore.EF` | EF Core database lifecycle | +| `FEFF.TestFixtures.AspNetCore.SignalR` | SignalR client for testing | + +## Supported Platforms + +- **.NET**: 8.0, 10.0 +- **Test Frameworks**: xUnit v3, TUnit + +## Resources + +- [GitHub Repository](https://github.com/metacoder-feff/FEFF.TestFixtures) +- [NuGet Packages](https://www.nuget.org/packages/FEFF.TestFixtures) +- [Examples](https://github.com/metacoder-feff/FEFF.TestFixtures/tree/main/examples) +- [Issue Tracker](https://github.com/metacoder-feff/FEFF.TestFixtures/issues) + +## What's Next? + +Choose your path based on your needs: + +| Your Goal | Next Step | +|-----------|-----------| +| Quick setup with xUnit v3 | [Quick Start (xUnit v3)](quick-start-xunit.md) | +| Quick setup with TUnit | [Quick Start (TUnit)](quick-start-tunit.md) | +| Create your own fixture | [Creating Custom Fixtures](creating-custom-fixtures.md) | +| Combine fixtures | [Fixture Dependencies](fixture-dependencies.md) | +| Explore builtin fixtures | [Fixture List](built-in-fixtures.md) | diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml new file mode 100644 index 0000000..56dc74d --- /dev/null +++ b/docs/articles/toc.yml @@ -0,0 +1,10 @@ +- name: Overview + href: overview.md +- name: Getting Started + href: 01.getting-started/toc.yml +- name: Advanced Usage + href: 02.advanced/toc.yml +# - name: Fixtures +# href: 03.fixtures/toc.yml +- name: Troubleshooting & FAQ + href: troubleshooting-faq.md \ No newline at end of file diff --git a/docs/articles/troubleshooting-faq.md b/docs/articles/troubleshooting-faq.md new file mode 100644 index 0000000..622d71e --- /dev/null +++ b/docs/articles/troubleshooting-faq.md @@ -0,0 +1,162 @@ +# Troubleshooting & FAQ + +This article addresses common questions and issues when working with FEFF.TestFixtures. + +## Which package should I use? + +Choose the package based on your test framework and requirements: + ++ **xUnit v3 projects**: Install `FEFF.TestFixtures.XunitV3` ++ **TUnit projects**: Install `FEFF.TestFixtures.TUnit` ++ **ASP.NET Core integration tests**: Also install `FEFF.TestFixtures.AspNetCore` + +### Package Reference Summary + +| Scenario | Package | +|----------|---------| +| xUnit v3 tests | `FEFF.TestFixtures.XunitV3` | +| TUnit tests | `FEFF.TestFixtures.TUnit` | +| ASP.NET Core tests | `FEFF.TestFixtures.AspNetCore` | +| ASP.NET Core + EF Core tests | `FEFF.TestFixtures.AspNetCore.EF` | +| ASP.NET Core + SignalR tests | `FEFF.TestFixtures.AspNetCore.SignalR` | + +## Why can't I resolve a fixture? + +If you get a compilation or runtime error when trying to resolve a fixture, ensure you've added the assembly-level attribute (xUnit v3 only): + +```csharp +[assembly: FEFF.TestFixtures.Xunit.TestFixturesExtension] +``` + +This attribute is required for xUnit v3 to initialize the extension and register fixtures with the test framework. + +> [!NOTE] +> TUnit users do not need this attribute. The `FEFF.TestFixtures.TUnit` package handles registration automatically. + +## Can fixtures share state across tests? + +Yes. Class-, collection-, and assembly-scoped fixtures are singleton within their scope. All tests within that scope receive the same instance. + +### Scope Types (for xUnit v3 integration) + +| Scope | Behavior | +|-------|----------| +| `test-case` (default) | New instance for each test method | +| `class` | One instance per test class | +| `collection` | One instance per test collection | +| `assembly` | One instance for the entire test assembly | + +### Example: Class-scoped Fixture + +```csharp +public class MyTests +{ + // Class-scoped fixture - shared across all tests in this class + protected MyFixture Shared { get; } = + TestContext.Current.GetFeffFixture(FixtureScopeType.Class); + + [Fact] + public void Test1() { /* uses Shared */ } + + [Fact] + public void Test2() { /* uses same Shared instance */ } +} +``` + +## How do I clean up resources in a fixture? + +Implement `IDisposable` or `IAsyncDisposable` on your fixture class. The engine will call `Dispose()` or `DisposeAsync()` at the end of the fixture's scope. + +### Synchronous Disposal + +```csharp +[Fixture] +public class TmpDirectoryFixture : IDisposable +{ + public string Path { get; } = Directory.CreateTempSubdirectory().FullName; + + public void Dispose() + { + try + { + Directory.Delete(Path, true); + } + catch (DirectoryNotFoundException) + { + // Ignore if already deleted + } + } +} +``` + +### Asynchronous Disposal + +```csharp +[Fixture] +public class DatabaseFixture : IAsyncDisposable +{ + private readonly IDbConnection _connection; + + public async ValueTask DisposeAsync() + { + await _connection.CloseAsync(); + await _connection.DisposeAsync(); + } +} +``` + +> [!TIP] +> Use `IAsyncDisposable` for fixtures that manage async resources like database connections, network sockets, or file handles. + +## Common Issues + +### Fixture Dependency Cycles + +Fixtures cannot have cyclic dependencies. The following will cause an error: + +```csharp +[Fixture] +public class FixtureA +{ + public FixtureA(FixtureB b) { } // Error: Circular dependency +} + +[Fixture] +public class FixtureB +{ + public FixtureB(FixtureA a) { } +} +``` + +**Solution**: Refactor to break the cycle. Consider introducing a third fixture that both depend on. + +### Fixture Not Found at Runtime + +If you get an exception about a missing fixture: + +1. Verify the fixture type has the `[Fixture]` attribute +2. Ensure the fixture is in an assembly that's referenced by your test project +3. Check that all fixture dependencies are also properly registered + +### Multiple Fixture Instances in Same Scope + +If you notice unexpected multiple instances of a fixture: + +```csharp +var fx1 = TestContext.Current.GetFeffFixture(); +var fx2 = TestContext.Current.GetFeffFixture(); + +// fx1 and fx2 should be the same instance in the same scope +``` + +Ensure you're using the same scope type and not accidentally creating new scopes. + +## Where to Report Issues or Ask Questions + ++ **Bug Reports**: [GitHub Issues](https://github.com/metacoder-feff/FEFF.TestFixtures/issues) ++ **Questions & Discussions**: [GitHub Discussions](https://github.com/metacoder-feff/FEFF.TestFixtures/discussions) + +Before opening a new issue, please: +1. Search existing issues to avoid duplicates +2. Include your .NET version, test framework version, and FEFF.TestFixtures version +3. Provide a minimal reproducible example when possible diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..e3bcc4f --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "src": "../src", + "files": [ + "**/*.csproj" + ] + } + ], + "dest": "api" + } + ], + "build": { + "content": [ + { + "files": [ + "**/*.{md,yml}" + ], + "exclude": [ + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "output": "_site", + "template": [ + "default", + "modern" + ], + "globalMetadata": { + "_appName": "FEFF.TestFixtures Library Documentation", + "_appTitle": "FEFF.TestFixtures Library Documentation", + "_enableSearch": true, + "pdf": false + } + } +} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ce141f3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,3 @@ +--- +redirect_url: articles/overview.html +--- \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..2992284 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,5 @@ +- name: Articles + href: articles/ +- name: API + href: api/ + homepage: api/index.md \ No newline at end of file diff --git a/examples/ExampleTests.AspNetCore/ExampleTests.AspNetCore.csproj b/examples/ExampleTests.AspNetCore/ExampleTests.AspNetCore.csproj index 0956dc6..3804c35 100644 --- a/examples/ExampleTests.AspNetCore/ExampleTests.AspNetCore.csproj +++ b/examples/ExampleTests.AspNetCore/ExampleTests.AspNetCore.csproj @@ -21,7 +21,8 @@ - + + diff --git a/examples/ExampleTests.AspNetCore/packages.lock.json b/examples/ExampleTests.AspNetCore/packages.lock.json index 089b8c5..5f2ad7a 100644 --- a/examples/ExampleTests.AspNetCore/packages.lock.json +++ b/examples/ExampleTests.AspNetCore/packages.lock.json @@ -51,13 +51,26 @@ "xunit.v3.extensibility.core": "3.2.2" } }, - "xunit.v3": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, + "xunit.v3.mtp-v2": { "type": "Direct", "requested": "[3.2.2, )", "resolved": "3.2.2", - "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "contentHash": "S0LJpeMIMrmbVLXDCvPVX47OLk28qBYfGU+5SNCbarOEdw8oKLfiVqaACwuYRvLiOqDEB/+VJ8gTSB1ZwheoOQ==", "dependencies": { - "xunit.v3.mtp-v1": "[3.2.2]" + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v2": "[3.2.2]" } }, "FEFF.TestFixtures": { @@ -115,6 +128,11 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.5", + "contentHash": "Cq0DLpL8oQmXX3EUCClAYWDBy7Nf3Km6kmUw/eYWlYcTeC3g3Nekd/Z/ldsiy+Oi3xboanlQV9oaVCkgdLEhOQ==" + }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", "resolved": "10.0.5", @@ -493,32 +511,32 @@ }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "resolved": "2.0.2", + "contentHash": "H580BvHyuADoWzlH9zRk5fqVyGucm6mhph+k40CQc9O4ie+Buxa4Pk9Q92BEClqIICqi25J7fuMII9qFYYgKtw==", "dependencies": { "Microsoft.ApplicationInsights": "2.23.0", - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "resolved": "2.0.2", + "contentHash": "MrHYdPZ1CiyYp5bfjzNSghfVwl/I9osMazcZMAbwZY0BhR32i70YLf4zSXECvU2qt2PvDdrjYpGRgBscFbjDpw==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + "resolved": "2.1.0", + "contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg==" }, "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "resolved": "2.0.2", + "contentHash": "2zKkQKaUoaKgb/3AekboWOdLMh4upCo1nLWQnjGzp8r9YjiNOZRrzTsJQ3A4U03AcbH0evlIvFDKYSUqmTVuug==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Win32.Registry": { @@ -577,15 +595,15 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "xunit.v3.core.mtp-v1": { + "xunit.v3.core.mtp-v2": { "type": "Transitive", "resolved": "3.2.2", - "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "contentHash": "zW82tdCm+T1uUD1JKE+SmhgMq8nCAvcFPRLIVEiRgaxBSjcyJEKopLU3bHGOa416q+N3Dz7m1zLoPR5VJ5OQ+Q==", "dependencies": { - "Microsoft.Testing.Extensions.Telemetry": "1.9.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", - "Microsoft.Testing.Platform": "1.9.1", - "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "Microsoft.Testing.Extensions.Telemetry": "2.0.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.0.2", + "Microsoft.Testing.Platform": "2.0.2", + "Microsoft.Testing.Platform.MSBuild": "2.0.2", "xunit.v3.extensibility.core": "[3.2.2]", "xunit.v3.runner.inproc.console": "[3.2.2]" } @@ -598,16 +616,6 @@ "xunit.v3.common": "[3.2.2]" } }, - "xunit.v3.mtp-v1": { - "type": "Transitive", - "resolved": "3.2.2", - "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", - "dependencies": { - "xunit.analyzers": "1.27.0", - "xunit.v3.assert": "[3.2.2]", - "xunit.v3.core.mtp-v1": "[3.2.2]" - } - }, "xunit.v3.runner.common": { "type": "Transitive", "resolved": "3.2.2", diff --git a/examples/ExampleTests.TUnit/ExampleTests.TUnit.csproj b/examples/ExampleTests.TUnit/ExampleTests.TUnit.csproj index 393de58..bba8988 100644 --- a/examples/ExampleTests.TUnit/ExampleTests.TUnit.csproj +++ b/examples/ExampleTests.TUnit/ExampleTests.TUnit.csproj @@ -9,6 +9,7 @@ + diff --git a/examples/ExampleTests.TUnit/packages.lock.json b/examples/ExampleTests.TUnit/packages.lock.json index fbfca62..e72f661 100644 --- a/examples/ExampleTests.TUnit/packages.lock.json +++ b/examples/ExampleTests.TUnit/packages.lock.json @@ -19,17 +19,28 @@ "TUnit.Core": "1.30.0" } }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, "TUnit": { "type": "Direct", "requested": "[1.*, )", - "resolved": "1.33.0", - "contentHash": "Ub/pwSJHsDzFL7Ebi2g/qr+QadNhyGTbBhST+yUUXzrB8VqTPOZiMZT7nT4BWR6WHWHA/9nL3Fv8Sc9OyLe2Kg==", + "resolved": "1.34.0", + "contentHash": "oEad7SW2iuW8eFUlQ3HlLHtadwwZ7q6nE5jbTp11A9Xw8Oby/8s6CnQHVlVigF/QxzZjRYZXDscHMdBV+GqzeA==", "dependencies": { "Microsoft.Testing.Extensions.CodeCoverage": "18.6.2", "Microsoft.Testing.Extensions.Telemetry": "2.2.1", "Microsoft.Testing.Extensions.TrxReport": "2.2.1", - "TUnit.Assertions": "1.33.0", - "TUnit.Engine": "1.33.0" + "TUnit.Assertions": "1.34.0", + "TUnit.Engine": "1.34.0" } }, "EnumerableAsyncProcessor": { @@ -156,16 +167,6 @@ "resolved": "10.0.5", "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Transitive", - "resolved": "18.6.2", - "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", - "dependencies": { - "Microsoft.DiaSymReader": "2.2.5", - "Microsoft.Extensions.DependencyModel": "8.0.2", - "Microsoft.Testing.Platform": "2.1.0" - } - }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", "resolved": "2.2.1", @@ -212,24 +213,24 @@ }, "TUnit.Assertions": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "RW1XPXo2DDOfj14QblXstgq+HtH1qtGM1z0k59YajTU4n02eBxQqROPPyM17qAcBU6r+MiwuPSPZlJGEKn4PJQ==" + "resolved": "1.34.0", + "contentHash": "3RVYPL2a8l3nZET25ukmTea+ibPxcafsPgrEia/HFKtR3N9yYTHGs5UsPw9f2hsbIEsWAjnbhrmTrsmz89HEwg==" }, "TUnit.Core": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "m8wxehfDvUqLtcRdKKzh+hnbtukSLd5A8/LfhDojzB03cEEJjcA1/NCUUEhikp0HTRHDIccB9wldbFSq0PUWlA==" + "resolved": "1.34.0", + "contentHash": "DFwbS3f0LdQNaO33MwGMtlQ/+Ik7XeG5mrhf7AfkRVOiGYdbaNtF77R1Ror45G3HOoGTTFm1ZhRShhlEWoqXNQ==" }, "TUnit.Engine": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "xCU7IYUoQjzBstG+EgdN6V4SyOXhXvg/bC6+KNADaOt+GXm7Vx0xeyvWvlU7bk8ShGdWwyMqOXDuAF058Tsiug==", + "resolved": "1.34.0", + "contentHash": "mLiX8fG2J+/4S6kAyOrbzYpKEMCb5W9JPSJwOgA7xieb0hasqjYXs5pJ9JSphgZcSnbsz28IszSnw/WHHL9P7A==", "dependencies": { "EnumerableAsyncProcessor": "3.8.4", "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.1", "Microsoft.Testing.Platform": "2.2.1", "Microsoft.Testing.Platform.MSBuild": "2.2.1", - "TUnit.Core": "1.33.0" + "TUnit.Core": "1.34.0" } } } diff --git a/examples/ExampleTests.XunitV3/ExampleTests.XunitV3.csproj b/examples/ExampleTests.XunitV3/ExampleTests.XunitV3.csproj index 5b05c68..56235e4 100644 --- a/examples/ExampleTests.XunitV3/ExampleTests.XunitV3.csproj +++ b/examples/ExampleTests.XunitV3/ExampleTests.XunitV3.csproj @@ -21,7 +21,8 @@ - + + diff --git a/examples/ExampleTests.XunitV3/packages.lock.json b/examples/ExampleTests.XunitV3/packages.lock.json index 9a4435e..e8a2d24 100644 --- a/examples/ExampleTests.XunitV3/packages.lock.json +++ b/examples/ExampleTests.XunitV3/packages.lock.json @@ -29,13 +29,26 @@ "xunit.v3.extensibility.core": "3.2.2" } }, - "xunit.v3": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, + "xunit.v3.mtp-v2": { "type": "Direct", "requested": "[3.2.2, )", "resolved": "3.2.2", - "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "contentHash": "S0LJpeMIMrmbVLXDCvPVX47OLk28qBYfGU+5SNCbarOEdw8oKLfiVqaACwuYRvLiOqDEB/+VJ8gTSB1ZwheoOQ==", "dependencies": { - "xunit.v3.mtp-v1": "[3.2.2]" + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v2": "[3.2.2]" } }, "FEFF.TestFixtures": { @@ -78,6 +91,11 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.5", + "contentHash": "Cq0DLpL8oQmXX3EUCClAYWDBy7Nf3Km6kmUw/eYWlYcTeC3g3Nekd/Z/ldsiy+Oi3xboanlQV9oaVCkgdLEhOQ==" + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "10.0.5", @@ -126,6 +144,11 @@ "resolved": "10.0.5", "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, "Microsoft.Extensions.Options": { "type": "Transitive", "resolved": "10.0.5", @@ -154,32 +177,32 @@ }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "resolved": "2.0.2", + "contentHash": "H580BvHyuADoWzlH9zRk5fqVyGucm6mhph+k40CQc9O4ie+Buxa4Pk9Q92BEClqIICqi25J7fuMII9qFYYgKtw==", "dependencies": { "Microsoft.ApplicationInsights": "2.23.0", - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "resolved": "2.0.2", + "contentHash": "MrHYdPZ1CiyYp5bfjzNSghfVwl/I9osMazcZMAbwZY0BhR32i70YLf4zSXECvU2qt2PvDdrjYpGRgBscFbjDpw==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + "resolved": "2.1.0", + "contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg==" }, "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "resolved": "2.0.2", + "contentHash": "2zKkQKaUoaKgb/3AekboWOdLMh4upCo1nLWQnjGzp8r9YjiNOZRrzTsJQ3A4U03AcbH0evlIvFDKYSUqmTVuug==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Win32.Registry": { @@ -215,15 +238,15 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "xunit.v3.core.mtp-v1": { + "xunit.v3.core.mtp-v2": { "type": "Transitive", "resolved": "3.2.2", - "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "contentHash": "zW82tdCm+T1uUD1JKE+SmhgMq8nCAvcFPRLIVEiRgaxBSjcyJEKopLU3bHGOa416q+N3Dz7m1zLoPR5VJ5OQ+Q==", "dependencies": { - "Microsoft.Testing.Extensions.Telemetry": "1.9.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", - "Microsoft.Testing.Platform": "1.9.1", - "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "Microsoft.Testing.Extensions.Telemetry": "2.0.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.0.2", + "Microsoft.Testing.Platform": "2.0.2", + "Microsoft.Testing.Platform.MSBuild": "2.0.2", "xunit.v3.extensibility.core": "[3.2.2]", "xunit.v3.runner.inproc.console": "[3.2.2]" } @@ -236,16 +259,6 @@ "xunit.v3.common": "[3.2.2]" } }, - "xunit.v3.mtp-v1": { - "type": "Transitive", - "resolved": "3.2.2", - "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", - "dependencies": { - "xunit.analyzers": "1.27.0", - "xunit.v3.assert": "[3.2.2]", - "xunit.v3.core.mtp-v1": "[3.2.2]" - } - }, "xunit.v3.runner.common": { "type": "Transitive", "resolved": "3.2.2", diff --git a/tests.runsettings b/tests.runsettings index 0f7b9df..1788fac 100644 --- a/tests.runsettings +++ b/tests.runsettings @@ -17,9 +17,12 @@ https://gist.github.com/osmyn/affc8841111283dc8b3ca1a95ace0dc2 + /obj/.*\.generated\.cs$ + /obj/.*\.g\.cs$ - - /tests/ + + ^/tests/ + ^/examples/ diff --git a/tests/FEFF.TestFixtures.ApiVerification.Tests/FEFF.TestFixtures.ApiVerification.Tests.csproj b/tests/FEFF.TestFixtures.ApiVerification.Tests/FEFF.TestFixtures.ApiVerification.Tests.csproj index cc88da3..4c4e3a5 100644 --- a/tests/FEFF.TestFixtures.ApiVerification.Tests/FEFF.TestFixtures.ApiVerification.Tests.csproj +++ b/tests/FEFF.TestFixtures.ApiVerification.Tests/FEFF.TestFixtures.ApiVerification.Tests.csproj @@ -27,7 +27,8 @@ - + + diff --git a/tests/FEFF.TestFixtures.ApiVerification.Tests/packages.lock.json b/tests/FEFF.TestFixtures.ApiVerification.Tests/packages.lock.json index 155e656..94d60ac 100644 --- a/tests/FEFF.TestFixtures.ApiVerification.Tests/packages.lock.json +++ b/tests/FEFF.TestFixtures.ApiVerification.Tests/packages.lock.json @@ -18,6 +18,17 @@ "Newtonsoft.Json": "13.0.1" } }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, "PublicApiGenerator": { "type": "Direct", "requested": "[11.5.4, )", @@ -52,13 +63,15 @@ "xunit.v3.extensibility.core": "3.2.2" } }, - "xunit.v3": { + "xunit.v3.mtp-v2": { "type": "Direct", "requested": "[3.2.2, )", "resolved": "3.2.2", - "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "contentHash": "S0LJpeMIMrmbVLXDCvPVX47OLk28qBYfGU+5SNCbarOEdw8oKLfiVqaACwuYRvLiOqDEB/+VJ8gTSB1ZwheoOQ==", "dependencies": { - "xunit.v3.mtp-v1": "[3.2.2]" + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v2": "[3.2.2]" } }, "Argon": { @@ -172,6 +185,11 @@ "resolved": "10.0.5", "contentHash": "hQB3Hq1LlF0NkGVNyZIvwIQIY3LM7Cw1oYjNiTvdNqmzzipVAWEK1c5sj2H5aFX0udnjgPLxSYKq2fupueS8ow==" }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.5", + "contentHash": "Cq0DLpL8oQmXX3EUCClAYWDBy7Nf3Km6kmUw/eYWlYcTeC3g3Nekd/Z/ldsiy+Oi3xboanlQV9oaVCkgdLEhOQ==" + }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", "resolved": "10.0.5", @@ -544,32 +562,32 @@ }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "resolved": "2.0.2", + "contentHash": "H580BvHyuADoWzlH9zRk5fqVyGucm6mhph+k40CQc9O4ie+Buxa4Pk9Q92BEClqIICqi25J7fuMII9qFYYgKtw==", "dependencies": { "Microsoft.ApplicationInsights": "2.23.0", - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "resolved": "2.0.2", + "contentHash": "MrHYdPZ1CiyYp5bfjzNSghfVwl/I9osMazcZMAbwZY0BhR32i70YLf4zSXECvU2qt2PvDdrjYpGRgBscFbjDpw==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + "resolved": "2.1.0", + "contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg==" }, "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "resolved": "2.0.2", + "contentHash": "2zKkQKaUoaKgb/3AekboWOdLMh4upCo1nLWQnjGzp8r9YjiNOZRrzTsJQ3A4U03AcbH0evlIvFDKYSUqmTVuug==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Win32.Registry": { @@ -640,15 +658,15 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "xunit.v3.core.mtp-v1": { + "xunit.v3.core.mtp-v2": { "type": "Transitive", "resolved": "3.2.2", - "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "contentHash": "zW82tdCm+T1uUD1JKE+SmhgMq8nCAvcFPRLIVEiRgaxBSjcyJEKopLU3bHGOa416q+N3Dz7m1zLoPR5VJ5OQ+Q==", "dependencies": { - "Microsoft.Testing.Extensions.Telemetry": "1.9.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", - "Microsoft.Testing.Platform": "1.9.1", - "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "Microsoft.Testing.Extensions.Telemetry": "2.0.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.0.2", + "Microsoft.Testing.Platform": "2.0.2", + "Microsoft.Testing.Platform.MSBuild": "2.0.2", "xunit.v3.extensibility.core": "[3.2.2]", "xunit.v3.runner.inproc.console": "[3.2.2]" } @@ -661,16 +679,6 @@ "xunit.v3.common": "[3.2.2]" } }, - "xunit.v3.mtp-v1": { - "type": "Transitive", - "resolved": "3.2.2", - "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", - "dependencies": { - "xunit.analyzers": "1.27.0", - "xunit.v3.assert": "[3.2.2]", - "xunit.v3.core.mtp-v1": "[3.2.2]" - } - }, "xunit.v3.runner.common": { "type": "Transitive", "resolved": "3.2.2", diff --git a/tests/FEFF.TestFixtures.TUnit.Tests/FEFF.TestFixtures.TUnit.Tests.csproj b/tests/FEFF.TestFixtures.TUnit.Tests/FEFF.TestFixtures.TUnit.Tests.csproj index 00827f1..b3f480b 100644 --- a/tests/FEFF.TestFixtures.TUnit.Tests/FEFF.TestFixtures.TUnit.Tests.csproj +++ b/tests/FEFF.TestFixtures.TUnit.Tests/FEFF.TestFixtures.TUnit.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/FEFF.TestFixtures.TUnit.Tests/packages.lock.json b/tests/FEFF.TestFixtures.TUnit.Tests/packages.lock.json index 238f22d..d3a9113 100644 --- a/tests/FEFF.TestFixtures.TUnit.Tests/packages.lock.json +++ b/tests/FEFF.TestFixtures.TUnit.Tests/packages.lock.json @@ -18,17 +18,28 @@ "Newtonsoft.Json": "13.0.1" } }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, "TUnit": { "type": "Direct", "requested": "[1.*, )", - "resolved": "1.33.0", - "contentHash": "Ub/pwSJHsDzFL7Ebi2g/qr+QadNhyGTbBhST+yUUXzrB8VqTPOZiMZT7nT4BWR6WHWHA/9nL3Fv8Sc9OyLe2Kg==", + "resolved": "1.34.0", + "contentHash": "oEad7SW2iuW8eFUlQ3HlLHtadwwZ7q6nE5jbTp11A9Xw8Oby/8s6CnQHVlVigF/QxzZjRYZXDscHMdBV+GqzeA==", "dependencies": { "Microsoft.Testing.Extensions.CodeCoverage": "18.6.2", "Microsoft.Testing.Extensions.Telemetry": "2.2.1", "Microsoft.Testing.Extensions.TrxReport": "2.2.1", - "TUnit.Assertions": "1.33.0", - "TUnit.Engine": "1.33.0" + "TUnit.Assertions": "1.34.0", + "TUnit.Engine": "1.34.0" } }, "EnumerableAsyncProcessor": { @@ -125,16 +136,6 @@ "resolved": "10.0.5", "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Transitive", - "resolved": "18.6.2", - "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", - "dependencies": { - "Microsoft.DiaSymReader": "2.2.5", - "Microsoft.Extensions.DependencyModel": "8.0.2", - "Microsoft.Testing.Platform": "2.1.0" - } - }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", "resolved": "2.2.1", @@ -186,24 +187,24 @@ }, "TUnit.Assertions": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "RW1XPXo2DDOfj14QblXstgq+HtH1qtGM1z0k59YajTU4n02eBxQqROPPyM17qAcBU6r+MiwuPSPZlJGEKn4PJQ==" + "resolved": "1.34.0", + "contentHash": "3RVYPL2a8l3nZET25ukmTea+ibPxcafsPgrEia/HFKtR3N9yYTHGs5UsPw9f2hsbIEsWAjnbhrmTrsmz89HEwg==" }, "TUnit.Core": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "m8wxehfDvUqLtcRdKKzh+hnbtukSLd5A8/LfhDojzB03cEEJjcA1/NCUUEhikp0HTRHDIccB9wldbFSq0PUWlA==" + "resolved": "1.34.0", + "contentHash": "DFwbS3f0LdQNaO33MwGMtlQ/+Ik7XeG5mrhf7AfkRVOiGYdbaNtF77R1Ror45G3HOoGTTFm1ZhRShhlEWoqXNQ==" }, "TUnit.Engine": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "xCU7IYUoQjzBstG+EgdN6V4SyOXhXvg/bC6+KNADaOt+GXm7Vx0xeyvWvlU7bk8ShGdWwyMqOXDuAF058Tsiug==", + "resolved": "1.34.0", + "contentHash": "mLiX8fG2J+/4S6kAyOrbzYpKEMCb5W9JPSJwOgA7xieb0hasqjYXs5pJ9JSphgZcSnbsz28IszSnw/WHHL9P7A==", "dependencies": { "EnumerableAsyncProcessor": "3.8.4", "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.1", "Microsoft.Testing.Platform": "2.2.1", "Microsoft.Testing.Platform.MSBuild": "2.2.1", - "TUnit.Core": "1.33.0" + "TUnit.Core": "1.34.0" } }, "feff.testfixtures": { @@ -243,6 +244,7 @@ "dependencies": { "AwesomeAssertions": "[9.4.0, )", "FEFF.TestFixtures.TUnit": "[1.4.3, )", + "Microsoft.Testing.Extensions.CodeCoverage": "[18.6.2, )", "TUnit": "[1.*, )" } } diff --git a/tests/FEFF.TestFixtures.Tests/FEFF.TestFixtures.Tests.csproj b/tests/FEFF.TestFixtures.Tests/FEFF.TestFixtures.Tests.csproj index 2e48436..02e9a48 100644 --- a/tests/FEFF.TestFixtures.Tests/FEFF.TestFixtures.Tests.csproj +++ b/tests/FEFF.TestFixtures.Tests/FEFF.TestFixtures.Tests.csproj @@ -27,7 +27,8 @@ - + + diff --git a/tests/FEFF.TestFixtures.Tests/packages.lock.json b/tests/FEFF.TestFixtures.Tests/packages.lock.json index 73cf92f..3955428 100644 --- a/tests/FEFF.TestFixtures.Tests/packages.lock.json +++ b/tests/FEFF.TestFixtures.Tests/packages.lock.json @@ -18,13 +18,26 @@ "Newtonsoft.Json": "13.0.1" } }, - "xunit.v3": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, + "xunit.v3.mtp-v2": { "type": "Direct", "requested": "[3.2.2, )", "resolved": "3.2.2", - "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "contentHash": "S0LJpeMIMrmbVLXDCvPVX47OLk28qBYfGU+5SNCbarOEdw8oKLfiVqaACwuYRvLiOqDEB/+VJ8gTSB1ZwheoOQ==", "dependencies": { - "xunit.v3.mtp-v1": "[3.2.2]" + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v2": "[3.2.2]" } }, "Microsoft.ApplicationInsights": { @@ -115,6 +128,11 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.5", + "contentHash": "Cq0DLpL8oQmXX3EUCClAYWDBy7Nf3Km6kmUw/eYWlYcTeC3g3Nekd/Z/ldsiy+Oi3xboanlQV9oaVCkgdLEhOQ==" + }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", "resolved": "10.0.5", @@ -498,32 +516,32 @@ }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "resolved": "2.0.2", + "contentHash": "H580BvHyuADoWzlH9zRk5fqVyGucm6mhph+k40CQc9O4ie+Buxa4Pk9Q92BEClqIICqi25J7fuMII9qFYYgKtw==", "dependencies": { "Microsoft.ApplicationInsights": "2.23.0", - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "resolved": "2.0.2", + "contentHash": "MrHYdPZ1CiyYp5bfjzNSghfVwl/I9osMazcZMAbwZY0BhR32i70YLf4zSXECvU2qt2PvDdrjYpGRgBscFbjDpw==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + "resolved": "2.1.0", + "contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg==" }, "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "resolved": "2.0.2", + "contentHash": "2zKkQKaUoaKgb/3AekboWOdLMh4upCo1nLWQnjGzp8r9YjiNOZRrzTsJQ3A4U03AcbH0evlIvFDKYSUqmTVuug==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Win32.Registry": { @@ -582,15 +600,15 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "xunit.v3.core.mtp-v1": { + "xunit.v3.core.mtp-v2": { "type": "Transitive", "resolved": "3.2.2", - "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "contentHash": "zW82tdCm+T1uUD1JKE+SmhgMq8nCAvcFPRLIVEiRgaxBSjcyJEKopLU3bHGOa416q+N3Dz7m1zLoPR5VJ5OQ+Q==", "dependencies": { - "Microsoft.Testing.Extensions.Telemetry": "1.9.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", - "Microsoft.Testing.Platform": "1.9.1", - "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "Microsoft.Testing.Extensions.Telemetry": "2.0.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.0.2", + "Microsoft.Testing.Platform": "2.0.2", + "Microsoft.Testing.Platform.MSBuild": "2.0.2", "xunit.v3.extensibility.core": "[3.2.2]", "xunit.v3.runner.inproc.console": "[3.2.2]" } @@ -603,16 +621,6 @@ "xunit.v3.common": "[3.2.2]" } }, - "xunit.v3.mtp-v1": { - "type": "Transitive", - "resolved": "3.2.2", - "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", - "dependencies": { - "xunit.analyzers": "1.27.0", - "xunit.v3.assert": "[3.2.2]", - "xunit.v3.core.mtp-v1": "[3.2.2]" - } - }, "xunit.v3.runner.common": { "type": "Transitive", "resolved": "3.2.2", diff --git a/tests/FEFF.TestFixtures.XunitV3.Tests/FEFF.TestFixtures.XunitV3.Tests.csproj b/tests/FEFF.TestFixtures.XunitV3.Tests/FEFF.TestFixtures.XunitV3.Tests.csproj index 58f2aa3..7f8dddc 100644 --- a/tests/FEFF.TestFixtures.XunitV3.Tests/FEFF.TestFixtures.XunitV3.Tests.csproj +++ b/tests/FEFF.TestFixtures.XunitV3.Tests/FEFF.TestFixtures.XunitV3.Tests.csproj @@ -27,7 +27,8 @@ - + + diff --git a/tests/FEFF.TestFixtures.XunitV3.Tests/packages.lock.json b/tests/FEFF.TestFixtures.XunitV3.Tests/packages.lock.json index 8eee8c4..9b47325 100644 --- a/tests/FEFF.TestFixtures.XunitV3.Tests/packages.lock.json +++ b/tests/FEFF.TestFixtures.XunitV3.Tests/packages.lock.json @@ -18,13 +18,26 @@ "Newtonsoft.Json": "13.0.1" } }, - "xunit.v3": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, + "xunit.v3.mtp-v2": { "type": "Direct", "requested": "[3.2.2, )", "resolved": "3.2.2", - "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "contentHash": "S0LJpeMIMrmbVLXDCvPVX47OLk28qBYfGU+5SNCbarOEdw8oKLfiVqaACwuYRvLiOqDEB/+VJ8gTSB1ZwheoOQ==", "dependencies": { - "xunit.v3.mtp-v1": "[3.2.2]" + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v2": "[3.2.2]" } }, "Microsoft.ApplicationInsights": { @@ -37,6 +50,11 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.5", + "contentHash": "Cq0DLpL8oQmXX3EUCClAYWDBy7Nf3Km6kmUw/eYWlYcTeC3g3Nekd/Z/ldsiy+Oi3xboanlQV9oaVCkgdLEhOQ==" + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "10.0.5", @@ -85,6 +103,11 @@ "resolved": "10.0.5", "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, "Microsoft.Extensions.Options": { "type": "Transitive", "resolved": "10.0.5", @@ -113,32 +136,32 @@ }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "resolved": "2.0.2", + "contentHash": "H580BvHyuADoWzlH9zRk5fqVyGucm6mhph+k40CQc9O4ie+Buxa4Pk9Q92BEClqIICqi25J7fuMII9qFYYgKtw==", "dependencies": { "Microsoft.ApplicationInsights": "2.23.0", - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "resolved": "2.0.2", + "contentHash": "MrHYdPZ1CiyYp5bfjzNSghfVwl/I9osMazcZMAbwZY0BhR32i70YLf4zSXECvU2qt2PvDdrjYpGRgBscFbjDpw==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + "resolved": "2.1.0", + "contentHash": "aHkjNTGIA+Zbdw6RJgSFrbDrCjO0CgqpElqYcvkRSeUhBv2bKarnvU3ep786U7UqrPlArT/B7VmImRibJD0Zrg==" }, "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "1.9.1", - "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "resolved": "2.0.2", + "contentHash": "2zKkQKaUoaKgb/3AekboWOdLMh4upCo1nLWQnjGzp8r9YjiNOZRrzTsJQ3A4U03AcbH0evlIvFDKYSUqmTVuug==", "dependencies": { - "Microsoft.Testing.Platform": "1.9.1" + "Microsoft.Testing.Platform": "2.0.2" } }, "Microsoft.Win32.Registry": { @@ -174,15 +197,15 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "xunit.v3.core.mtp-v1": { + "xunit.v3.core.mtp-v2": { "type": "Transitive", "resolved": "3.2.2", - "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "contentHash": "zW82tdCm+T1uUD1JKE+SmhgMq8nCAvcFPRLIVEiRgaxBSjcyJEKopLU3bHGOa416q+N3Dz7m1zLoPR5VJ5OQ+Q==", "dependencies": { - "Microsoft.Testing.Extensions.Telemetry": "1.9.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", - "Microsoft.Testing.Platform": "1.9.1", - "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "Microsoft.Testing.Extensions.Telemetry": "2.0.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.0.2", + "Microsoft.Testing.Platform": "2.0.2", + "Microsoft.Testing.Platform.MSBuild": "2.0.2", "xunit.v3.extensibility.core": "[3.2.2]", "xunit.v3.runner.inproc.console": "[3.2.2]" } @@ -195,16 +218,6 @@ "xunit.v3.common": "[3.2.2]" } }, - "xunit.v3.mtp-v1": { - "type": "Transitive", - "resolved": "3.2.2", - "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", - "dependencies": { - "xunit.analyzers": "1.27.0", - "xunit.v3.assert": "[3.2.2]", - "xunit.v3.core.mtp-v1": "[3.2.2]" - } - }, "xunit.v3.runner.common": { "type": "Transitive", "resolved": "3.2.2", diff --git a/tests/Subjects/TUnit.TestSubject/TUnit.TestSubject.csproj b/tests/Subjects/TUnit.TestSubject/TUnit.TestSubject.csproj index 9694133..649790e 100644 --- a/tests/Subjects/TUnit.TestSubject/TUnit.TestSubject.csproj +++ b/tests/Subjects/TUnit.TestSubject/TUnit.TestSubject.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/Subjects/TUnit.TestSubject/packages.lock.json b/tests/Subjects/TUnit.TestSubject/packages.lock.json index 712f0d0..012cf53 100644 --- a/tests/Subjects/TUnit.TestSubject/packages.lock.json +++ b/tests/Subjects/TUnit.TestSubject/packages.lock.json @@ -8,17 +8,28 @@ "resolved": "9.4.0", "contentHash": "dJxkWiQ8D+xT6Gr2sSL83+Mar+Vpy2JTcUPxFcckpPJ8VYBfSgnk+zqpS6t7kcGnjz8NLyF14qfuoL4bKzzoew==" }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.6.2, )", + "resolved": "18.6.2", + "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.5", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Microsoft.Testing.Platform": "2.1.0" + } + }, "TUnit": { "type": "Direct", "requested": "[1.*, )", - "resolved": "1.33.0", - "contentHash": "Ub/pwSJHsDzFL7Ebi2g/qr+QadNhyGTbBhST+yUUXzrB8VqTPOZiMZT7nT4BWR6WHWHA/9nL3Fv8Sc9OyLe2Kg==", + "resolved": "1.34.0", + "contentHash": "oEad7SW2iuW8eFUlQ3HlLHtadwwZ7q6nE5jbTp11A9Xw8Oby/8s6CnQHVlVigF/QxzZjRYZXDscHMdBV+GqzeA==", "dependencies": { "Microsoft.Testing.Extensions.CodeCoverage": "18.6.2", "Microsoft.Testing.Extensions.Telemetry": "2.2.1", "Microsoft.Testing.Extensions.TrxReport": "2.2.1", - "TUnit.Assertions": "1.33.0", - "TUnit.Engine": "1.33.0" + "TUnit.Assertions": "1.34.0", + "TUnit.Engine": "1.34.0" } }, "EnumerableAsyncProcessor": { @@ -115,16 +126,6 @@ "resolved": "10.0.5", "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Transitive", - "resolved": "18.6.2", - "contentHash": "vRDhB96XQyVdYFp4cQZOMz/lx0okfCdzTXPxGiuFhKx2yUL0FT/skTpnTv+7x13+tjNOcT39i2Ln3BYtslzf2w==", - "dependencies": { - "Microsoft.DiaSymReader": "2.2.5", - "Microsoft.Extensions.DependencyModel": "8.0.2", - "Microsoft.Testing.Platform": "2.1.0" - } - }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", "resolved": "2.2.1", @@ -171,24 +172,24 @@ }, "TUnit.Assertions": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "RW1XPXo2DDOfj14QblXstgq+HtH1qtGM1z0k59YajTU4n02eBxQqROPPyM17qAcBU6r+MiwuPSPZlJGEKn4PJQ==" + "resolved": "1.34.0", + "contentHash": "3RVYPL2a8l3nZET25ukmTea+ibPxcafsPgrEia/HFKtR3N9yYTHGs5UsPw9f2hsbIEsWAjnbhrmTrsmz89HEwg==" }, "TUnit.Core": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "m8wxehfDvUqLtcRdKKzh+hnbtukSLd5A8/LfhDojzB03cEEJjcA1/NCUUEhikp0HTRHDIccB9wldbFSq0PUWlA==" + "resolved": "1.34.0", + "contentHash": "DFwbS3f0LdQNaO33MwGMtlQ/+Ik7XeG5mrhf7AfkRVOiGYdbaNtF77R1Ror45G3HOoGTTFm1ZhRShhlEWoqXNQ==" }, "TUnit.Engine": { "type": "Transitive", - "resolved": "1.33.0", - "contentHash": "xCU7IYUoQjzBstG+EgdN6V4SyOXhXvg/bC6+KNADaOt+GXm7Vx0xeyvWvlU7bk8ShGdWwyMqOXDuAF058Tsiug==", + "resolved": "1.34.0", + "contentHash": "mLiX8fG2J+/4S6kAyOrbzYpKEMCb5W9JPSJwOgA7xieb0hasqjYXs5pJ9JSphgZcSnbsz28IszSnw/WHHL9P7A==", "dependencies": { "EnumerableAsyncProcessor": "3.8.4", "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.1", "Microsoft.Testing.Platform": "2.2.1", "Microsoft.Testing.Platform.MSBuild": "2.2.1", - "TUnit.Core": "1.33.0" + "TUnit.Core": "1.34.0" } }, "feff.testfixtures": {