xchain 0.2.2-chain-name-and-order.5
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
<PackageReference Include="xchain" Version="0.2.2-chain-name-and-order.5" />
<PackageVersion Include="xchain" Version="0.2.2-chain-name-and-order.5" />
<PackageReference Include="xchain" />
paket add xchain --version 0.2.2-chain-name-and-order.5
#r "nuget: xchain, 0.2.2-chain-name-and-order.5"
#:package xchain@0.2.2-chain-name-and-order.5
#addin nuget:?package=xchain&version=0.2.2-chain-name-and-order.5&prerelease
#tool nuget:?package=xchain&version=0.2.2-chain-name-and-order.5&prerelease
Xchain
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.
๐งช Example 1: Ordered Execution with Link
attribute
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.
๐งช Example 3: Skipping on Error with Link
and LinkUnless
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
- Implement your own attribute class.
- Decorate it with
[TraitDiscoverer("Xchain.TestChainTraitDiscoverer", "Xchain")]
. - Add public properties โ these become traits.
- 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 | Versions 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. |
-
.NETStandard 2.1
- xunit (>= 2.9.3)
- xunit.abstractions (>= 2.0.3)
- xunit.extensibility.core (>= 2.9.3)
- Xunit.SkippableFact (>= 1.5.23)
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 |