![]() |
VOOZH | about |
dotnet add package Atc.Test --version 3.0.0
NuGet\Install-Package Atc.Test -Version 3.0.0
<PackageReference Include="Atc.Test" Version="3.0.0" />
<PackageVersion Include="Atc.Test" Version="3.0.0" />Directory.Packages.props
<PackageReference Include="Atc.Test" />Project file
paket add Atc.Test --version 3.0.0
#r "nuget: Atc.Test, 3.0.0"
#:package Atc.Test@3.0.0
#addin nuget:?package=Atc.Test&version=3.0.0Install as a Cake Addin
#tool nuget:?package=Atc.Test&version=3.0.0Install as a Cake Tool
Atc.Test is a .NET helper library that streamlines authoring tests with xUnit v3, AutoFixture, NSubstitute, and AwesomeAssertions. It provides rich data attributes, automatic specimen customization, and ergonomic frozen value reuse to reduce ceremony and improve test readability.
You can “just wire everything manually” with plain xUnit and hand‑rolled mocks—so why use this instead?
| Problem Without | What You Gain With Atc.Test |
Why It Matters Over Time |
|---|---|---|
| Repeating constructor/mocker boilerplate in every test | Parameter-only intent: you list just what the test cares about | Lower cognitive load; faster review – noise removed |
| Fragile refactors (add a ctor param ⇒ touch many files) | Fixture-driven auto‑supply of new dependencies | Constructor churn becomes O(1) instead of O(N tests) |
| Divergent ad‑hoc mock styles (naming, setup order) | Central factory + consistent frozen reuse semantics | Suite stays uniform; easier large-scale edits / audits |
| Accidental duplicate substitutes for logically single collaborator | [Frozen] exact-type reuse + early supplied promotion (member data) |
Prevents subtle mismatch bugs & expectation gaps |
| Manual re-creation of “shared conventions” (recursion handling, generators) | One-time customization via [AutoRegister] |
New test inherits standards automatically |
| AI-generated setup drifts over time | Declarative attributes act as a stable policy layer | Reduces maintenance & future prompt dependency |
Atc.Test trades a tiny amount of initial abstraction for compounding savings in refactors, readability, and consistency. AI can quickly generate boilerplate; this library’s value is eliminating the need for that boilerplate in the first place—and giving you a single, policy‑driven locus for customization and reuse.
AutoNSubstituteData, InlineAutoNSubstituteData, MemberAutoNSubstituteData, ClassAutoNSubstituteData.[Frozen] parameters).ICustomization / ISpecimenBuilder via [AutoRegister].Add Atc.Test to your test project along with explicit references to xUnit and the test SDK:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="xunit.v3" Version="3.2.2" />
<PackageReference Include="Atc.Test" Version="$(LatestOrPinned)" />
</ItemGroup>
</Project>
Atc.Test depends on xunit.v3.extensibility.core (the extensibility surface) but intentionally does not bring in the xunit.v3 meta-package:
If you want a different xUnit patch/minor version, change the <PackageReference Include="xunit.v3" ... /> line—no changes to Atc.Test required.
Atc.Test relies on xUnit v3 extensibility APIs:
ValueTask<IReadOnlyCollection<ITheoryDataRow>> GetData(...).ITheoryDataRow & metadata (Label, Explicit, Timeout) preservation.DisposalTracker parameter passed to data attributes.These do not exist in xUnit v2. Attempting to use a v2 framework or runner will result in discovery failures or compile errors.
| Scenario | Outcome |
|---|---|
Replace xunit.v3 with xunit (v2) |
Build errors: missing v3 types & method signatures |
| Run with legacy v2 runner | Test discovery fails (no v3 discovery support) |
Mix projects: some v2, some using Atc.Test |
Allowed, but they must not share v3-based base test classes |
Remove explicit xunit.v3 reference |
Build error / missing types (transitive reference intentionally absent) |
Optional guard rails (not included by default):
<Target Name="ValidateXunitV3" BeforeTargets="Build">
<Error Condition="!Exists('$(NuGetPackageRoot)xunit.v3/')"
Text="Atc.Test requires an explicit PackageReference to xunit.v3 in the test project." />
</Target>
“Why no v2 support?” the answer is simply that the library embraces the cleaner v3 data extensibility model; back-porting would require a parallel code path and reduce clarity.
public class CalculatorTests
{
[Theory]
[AutoNSubstituteData]
public void AutoData_Generates_Specimens(int a, int b, Calculator sut)
=> sut.Add(a, b).Should().Be(a + b);
[Theory]
[InlineAutoNSubstituteData(2, 3)]
public void InlineAutoData_Mixes_Inline_And_Auto(int a, int b, Calculator sut)
=> sut.Add(a, b).Should().Be(5);
public static IEnumerable<object?[]> MemberSource()
{
yield return new object?[] { 1, 2 };
yield return new object?[] { 10, 20 };
}
[Theory]
[MemberAutoNSubstituteData(nameof(MemberSource))]
public void MemberAutoData_Augments_Member_Data(int a, int b, Calculator sut)
=> sut.Add(a, b).Should().Be(a + b);
}
All remaining parameters (after inline/member supplied ones) are created via an AutoFixture IFixture that substitutes interfaces/abstract classes using NSubstitute.
Note NSubstitute is used automatically when the requested type is an interface or abstract class.
When you decorate a parameter with [Frozen], its resolved instance is reused for other specimens requiring that exact type. MemberAutoNSubstituteData adds exact-type promotion: reusing an earlier supplied value for a later [Frozen] parameter when that later slot was not part of the member row.
| Scenario | Attribute | Behavior |
|---|---|---|
| Positional frozen reuse | ClassAutoNSubstituteData & MemberAutoNSubstituteData |
If a value is supplied at the same index as a [Frozen] parameter, it is frozen and reused. |
| Exact-type promotion (member data only) | MemberAutoNSubstituteData |
Later [Frozen] T without a supplied value reuses an earlier supplied parameter whose declared type is exactly T. |
| No interface/base promotion | Both | Only exact parameter type matches are reused (no interface or base class widening). |
[Theory]
[InlineAutoNSubstituteData(42)]
public void Positional_Frozen_Reuses_Inline_Value(
[Frozen] int number,
SomeConsumer consumer)
{
consumer.NumberDependency.Should().Be(number);
}
public static IEnumerable<object?[]> ServiceRow()
{
yield return new object?[] { Substitute.For<IMyService>() }; // supplies parameter 0 only
}
[Theory]
[MemberAutoNSubstituteData(nameof(ServiceRow))]
public void Promotion_Reuses_Earlier_Same_Type(
IMyService supplied,
[Frozen] IMyService frozenLater,
NeedsService consumer)
{
frozenLater.Should().BeSameAs(supplied);
consumer.Service.Should().BeSameAs(supplied);
}
public interface IFoo {}
public interface IBar {}
public class DualImpl : IFoo, IBar {}
public static IEnumerable<object?[]> DualRow()
{
yield return new object?[] { new DualImpl() }; // supplies IFoo parameter only
}
[Theory]
[MemberAutoNSubstituteData(nameof(DualRow))]
public void Different_Interface_Not_Promoted(
IFoo foo,
[Frozen] IBar bar,
UsesBar consumer)
{
bar.Should().NotBeSameAs(foo); // separate instance
consumer.Bar.Should().BeSameAs(bar); // consumer wired to frozen IBar
}
Design Rationale:
Any ICustomization or ISpecimenBuilder decorated with [AutoRegister] is added automatically to the fixture created by FixtureFactory.Create().
Example:
[AutoRegister]
public class GuidCustomization : ICustomization
{
public void Customize(IFixture fixture) => fixture.Register(() => Guid.NewGuid());
}
| Helper | Purpose |
|---|---|
EquivalencyAssertionOptionsExtensions |
Adds convenience config (e.g., date precision) to AwesomeAssertions equivalency. |
SubstituteExtensions |
Inspect substitutes, wait for calls, retrieve arguments. |
TaskExtensions |
Await with timeouts. |
ObjectExtensions |
Access protected members via reflection helpers. |
FixtureFactory |
Central factory returning a consistently customized IFixture. |
| Aspect | Value |
|---|---|
| Test Framework | xUnit v3 (must be referenced directly) |
| Mocking | NSubstitute (transitively used for interfaces/abstract classes) |
| Assertions | AwesomeAssertions (recommended) |
As of v3.0.0, Atc.Test depends on AwesomeAssertions
(the Apache-2.0 community fork of FluentAssertions 7.x) instead of FluentAssertions, whose v8+
releases require a paid commercial license. To upgrade a consuming project:
using FluentAssertions; with using AwesomeAssertions; (and likewise for the
.Equivalency, .Primitives, .Execution, and .Extensions sub-namespaces, plus any <Using Include="FluentAssertions" /> global usings in your .csproj).<PackageReference Include="FluentAssertions" ... /> from your test projects —
Atc.Test brings in AwesomeAssertions transitively..Should()... assertions need no changes; the API is identical to FluentAssertions 7.x.| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 167 | 6/22/2026 |
| 2.0.17 | 9,457 | 2/8/2026 |
| 2.0.16 | 8,810 | 9/16/2025 |
| 1.1.18 | 21,874 | 3/1/2025 |
| 1.1.17 | 13,886 | 1/20/2025 |
| 1.1.12 | 510 | 1/19/2025 |
| 1.1.9 | 6,357 | 1/2/2025 |
| 1.1.4 | 10,223 | 10/9/2024 |
| 1.1.2 | 1,794 | 10/2/2024 |
| 1.0.107 | 469 | 10/1/2024 |
| 1.0.105 | 2,629 | 9/15/2024 |
| 1.0.102 | 618 | 9/14/2024 |
| 1.0.100 | 407 | 9/13/2024 |
| 1.0.98 | 678 | 9/10/2024 |
| 1.0.96 | 2,783 | 8/15/2024 |
| 1.0.93 | 1,096 | 8/15/2024 |
| 1.0.89 | 15,662 | 4/5/2024 |
| 1.0.79 | 25,501 | 9/5/2023 |
| 1.0.77 | 11,031 | 6/27/2023 |
| 1.0.75 | 5,443 | 4/28/2023 |