Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Create NuGet Packages Workflow
name: CI Build

on:
push:
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/doc-pages.yml
Original file line number Diff line number Diff line change
@@ -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
20 changes: 19 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -38,11 +45,22 @@ 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

- name: Build
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ tests/Files/API/*.received.*
.nupkg/
nupkg/

docs/_site/
docs/api/

###########################################################################
# Multiple sources ...
# 1. FROM: gitlab .Net template
Expand Down
79 changes: 4 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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/)
38 changes: 38 additions & 0 deletions _DevOps/test-cov.sh
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# FEFF.TestFixtures API Reference
54 changes: 54 additions & 0 deletions docs/articles/01.getting-started/built-in-fixtures.md
Original file line number Diff line number Diff line change
@@ -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) |
Loading