xchain 0.2.2-chain-name-and-order.5

This is a prerelease version of xchain.
There is a newer version of this package available.
See the version list below for details.
dotnet add package xchain --version 0.2.2-chain-name-and-order.5
                    
NuGet\Install-Package xchain -Version 0.2.2-chain-name-and-order.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="xchain" Version="0.2.2-chain-name-and-order.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="xchain" Version="0.2.2-chain-name-and-order.5" />
                    
Directory.Packages.props
<PackageReference Include="xchain" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add xchain --version 0.2.2-chain-name-and-order.5
                    
#r "nuget: xchain, 0.2.2-chain-name-and-order.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package xchain@0.2.2-chain-name-and-order.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=xchain&version=0.2.2-chain-name-and-order.5&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=xchain&version=0.2.2-chain-name-and-order.5&prerelease
                    
Install as a Cake Tool

Xchain

Build NuGet

Xchain extends xUnit with a fluent mechanism to chain tests, pass data between them, and skip dependent tests if earlier ones fail. It improves test readability and flow control when writing integration or system-level tests that have interdependencies.

Key Features

  • Custom test ordering via Link attribute
  • Shared output across chained tests using TestChainFixture
  • Failure-aware execution โ€” skip dependent tests if prior ones failed
  • Automatic exception capture for reporting/debugging
  • Flexible Trait metadata via attributes

๐Ÿงช Chained Test Execution with Xchain

This test collection demonstrates how Xchain tracks failures and skips dependent tests in a chain.

[TestCaseOrderer("Xchain.LinkOrderer", "Xchain")]
public class ChainTest(TestChainFixture chain) : IClassFixture<TestChainFixture>
{
    [ChainFact, Link(3)]
    public void Test1() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException(); // Skipped due to previous Exception
    });

    [ChainFact, Link(2)]
    public void Test2() => chain.LinkUnless<NotImplementedException>((output) =>
    {
        var sleep = output.Get<int>("Sleep");
        Thread.Sleep(sleep); // Succeeds, runs before NotImplementedException happens
    });

    [ChainFact, Link(1)]
    public void Test3() => chain.Link((output) =>
    {
        var sleep = 100;
        Thread.Sleep(sleep);
        output["Sleep"] = sleep * 2;
        throw new TimeoutException(); // Root failure
    });

    [ChainFact, Link(4)]
    public void Test4() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException(); // Skipped due to Test1 being skipped
    });

    [ChainFact, Link(5)]
    public void Test5() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException(); // Skipped due to Test4 being skipped
    });
}

๐Ÿ”Ž Output

Xchain.Tests.ChainTest.Test3              // โŒ Test3 failed (root failure)
  Source: ChainTest.cs line 22
  Duration: 104 ms
  Message:                                   
    System.TimeoutException : The operation has timed out.    

Xchain.Tests.ChainTest.Test2              // โœ… Test2 ran successfully
  Source: ChainTest.cs line 14
  Duration: 209 ms                                       

Xchain.Tests.ChainTest.Test1              // โš ๏ธ Skipped due to Test3 failure
  Source: ChainTest.cs line 7
  Duration: 1 ms
  Message:
    Test3 failed in ChainTest.cs line 22.
    The operation has timed out.                          

Xchain.Tests.ChainTest.Test4              // โš ๏ธ Skipped due to Test1 being skipped
  Source: ChainTest.cs line 32
  Duration: 1 ms
  Message:
    Test1 skipped in ChainTest.cs line 7.
    Test3 failed in ChainTest.cs line 22.
    The operation has timed out.                          

Xchain.Tests.ChainTest.Test5              // โš ๏ธ Skipped due to Test4 โ†’ Test1 โ†’ Test3 chain
  Source: ChainTest.cs line 38
  Duration: 1 ms
  Message:
    Test4 skipped in ChainTest.cs line 32.
    Test1 skipped in ChainTest.cs line 7.
    Test3 failed in ChainTest.cs line 22.
    The operation has timed out.                          

๐Ÿ“˜ Summary

  • Test3 fails with a TimeoutException โ€” it is the root failure.
  • Test2 succeeds โ€” it only skips on NotImplementedException, which did not occur.
  • Test1 is skipped because it depends on any Exception, and Test3 failed.
  • Test4 is skipped because Test1 was skipped.
  • Test5 is skipped because Test4 was skipped โ€” demonstrating deep chaining.

This demonstrates using the LinkOrderer to control test run sequence based on the provided order value.

[TestCaseOrderer("Xchain.LinkOrderer", "Xchain")]
public class ChainTest
{
    [Fact, Link(1)] public void Test1() => Thread.Sleep(1000);
    [Fact, Link(3)] public void Test2() => Thread.Sleep(3000);
    [Fact, Link(2)] public void Test3() => Thread.Sleep(2000);
}

โžก๏ธ What it shows: Tests will run in the order 1 โ†’ 3 โ†’ 2 based on their [Link(x)] values, not alphabetically or by name.

๐Ÿงช Example 2: Sharing Data with TestChainFixture

This example shows how test output values and errors can be shared across test methods using the fixture.

[TestCaseOrderer("Xchain.LinkOrderer", "Xchain")]
public class ChainTest(TestChainFixture testChain) : IClassFixture<TestChainFixture>
{
    [Fact, Link(3)]
    public void Test1() => testChain.Errors.Push(new NotImplementedException());

    [Fact, Link(2)]
    public void Test2()
    {
        var sleep = (int)testChain.Output["Sleep"];
        Thread.Sleep(sleep);
    }

    [Fact, Link(1)]
    public void Test3()
    {
        var sleep = 1000;
        testChain.Output["Sleep"] = sleep * 2;
        Thread.Sleep(sleep);
    }
}

โžก๏ธ What it shows:

  • Test3 sets shared output.
  • Test2 reads that output and uses it.
  • Test1 pushes an error manually to simulate a failure.

This uses the fluent API to automatically skip tests based on prior failures.

[TestCaseOrderer("Xchain.LinkOrderer", "Xchain")]
public class ChainTest(TestChainFixture chain) : IClassFixture<TestChainFixture>
{
    [ChainFact, Link(3)]
    public void Test1() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException();
    });


    [ChainFact, Link(2)]
    public void Test2() => chain.LinkUnless<NotImplementedException>((output) =>
    {
        var sleep = output.Get<int>("Sleep");
        Thread.Sleep(sleep);
    });
    

    [ChainFact, Link(1)]
    public void Test3() => chain.Link((output) =>
    {
        var sleep = 1000;
        Thread.Sleep(sleep);
        output["Sleep"] = sleep * 2;

        throw new TimeoutException();
    });
}

โžก๏ธ What it shows:

  • LinkUnless<T> skips the test if a matching exception occurred earlier.
  • Link captures and records exceptions for dependent tests to react to.

Xchain LinkUnless Async

Following code demonstrate usage of LinkUnlessAsync method.

[TestCaseOrderer("Xchain.LinkOrderer", "Xchain")]
public class ChainTest(TestChainFixture chain) : IClassFixture<TestChainFixture>
{
    [ChainFact, Link(3)]
    public void Test1() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException();
    });


    [ChainFact, Link(2)]
    public async Task Test2() => await chain.LinkUnlessAsync<NotImplementedException>(async (output, cancellationToken) =>
    {
        var sleep = output.Get<int>("Sleep");
        await Task.Delay(sleep, cancellationToken);
    });
    

    [ChainFact, Link(1)]
    public async Task Test3() => await chain.LinkAsync(async (output, cancellationToken) =>
    {
        const int sleep = 1000;
        output["Sleep"] = sleep;
        await Task.Delay(sleep, cancellationToken);
    }, TimeSpan.FromMilliseconds(100));

    [ChainFact, Link(4)]
    public void Test4() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException();
    });

    [ChainFact, Link(5)]
    public void Test5() => chain.LinkUnless<Exception>((output) =>
    {
        throw new NotImplementedException();
    });
}

๐Ÿ”— Flexible Trait Metadata via Attributes

xChain supports attaching custom metadata to your test methods using any attribute that implements ITraitAttribute. These attributes are automatically discovered and their public properties are converted into xUnit traits at runtime.

โœ… How It Works

  1. Implement your own attribute class.
  2. Decorate it with [TraitDiscoverer("Xchain.TestChainTraitDiscoverer", "Xchain")].
  3. Add public properties โ€” these become traits.
  4. Use the attribute on your test method.

๐Ÿงช Example: Define a Custom Attribute

using Xunit.Sdk;

[TraitDiscoverer("Xchain.TestChainTraitDiscoverer", "Xchain")]
[AttributeUsage(AttributeTargets.Method)]
public class ChainTagAttribute(string? owner = null, string? category = null, string? color = null)
    : Attribute, ITraitAttribute
{
    public string? Owner { get; set; } = owner;
    public string? Category { get; set; } = category;
    public string? Color { get; set; } = color;
}

This code demonstrates how to annotate any test case with custom metadata using ChainTag, which is automatically exposed as xUnit traits.


    [ChainFact, Link(1)]
    [ChainTag(Owner = "Kethoneinuo", Category = "Important", Color = "Black")]
    public async Task Test3() => await chain.LinkAsync(async (output, cancellationToken) =>
    {
        const int sleep = 1000;
        output["Sleep"] = sleep;
        await Task.Delay(sleep, cancellationToken);
    }, TimeSpan.FromMilliseconds(100));

Created from JandaBox

Box icon created by Freepik - Flaticon

Powered by Xunit.SkippableFact

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 449 6/29/2025
0.3.5-try-output.7 120 6/18/2025
0.3.5-try-output.2 121 6/18/2025
0.3.5-main.2 74 6/29/2025
0.3.5-main.1 122 6/18/2025
0.3.5-collection-link.18 69 6/29/2025
0.3.5-collection-link.17 71 6/29/2025
0.3.5-collection-link.12 285 6/19/2025
0.3.5-collection-link.6 119 6/18/2025
0.3.5-collection-link.5 117 6/18/2025
0.3.4 146 6/18/2025
0.3.4-test-output.3 118 6/18/2025
0.3.4-test-output.2 119 6/18/2025
0.3.4-test-output.1 121 6/18/2025
0.3.4-main.1 118 6/18/2025
0.3.3 231 6/16/2025
0.3.3-main.1 121 6/16/2025
0.3.3-chain-linker.7 129 6/16/2025
0.3.2 230 6/13/2025
0.3.2-main.2 194 6/13/2025
0.3.2-flow-fact.3 238 6/13/2025
0.3.2-chain-linker.6 263 6/11/2025
0.3.1 308 5/26/2025
0.3.1-skipped-reason-emoji.1 128 5/26/2025
0.3.1-main.1 126 5/26/2025
0.3.0 153 5/26/2025
0.2.2-main.1 128 5/26/2025
0.2.2-chain-name-and-order.7 80 5/25/2025
0.2.2-chain-name-and-order.5 46 5/24/2025
0.2.1 158 5/22/2025
0.2.1-trait-discoverer.1 120 5/21/2025
0.2.1-main.1 122 5/22/2025
0.2.0 165 5/19/2025
0.1.0 162 5/18/2025
0.1.0-test-chain.3 127 5/18/2025
0.1.0-main.4 122 5/18/2025
0.1.0-main.2 128 5/18/2025
0.1.0-ci.1 125 5/18/2025
0.1.0-chain-orderer.3 123 5/18/2025