Skip to content

feat: add benchmarks for Imposter and Mockolate mocking frameworks#5295

Merged
thomhurst merged 2 commits intothomhurst:mainfrom
vbreuss:topic/add-benchmarks-against-sourcegenerated-mocking-frameworks
Mar 29, 2026
Merged

feat: add benchmarks for Imposter and Mockolate mocking frameworks#5295
thomhurst merged 2 commits intothomhurst:mainfrom
vbreuss:topic/add-benchmarks-against-sourcegenerated-mocking-frameworks

Conversation

@vbreuss
Copy link
Copy Markdown
Contributor

@vbreuss vbreuss commented Mar 29, 2026

Description

This pull request expands the mocking libraries benchmarked in the TUnit.Mocks.Benchmarks project with two other source-generated mocking frameworks:

It introduces new benchmarks for these frameworks across all scenarios (mock creation, setup, invocation, callbacks, verification, and combined workflows) to provide a fair comparison between the libraries.

Related Issue

Fixes #

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Performance improvement
  • Refactoring (no functional changes)

Checklist

Required

  • I have read the Contributing Guidelines
  • If this is a new feature, I started a discussion first and received agreement
  • My code follows the project's code style (modern C# syntax, proper naming conventions)
  • I have written tests that prove my fix is effective or my feature works

TUnit-Specific Requirements

  • Dual-Mode Implementation: If this change affects test discovery/execution, I have implemented it in BOTH:
    • Source Generator path (TUnit.Core.SourceGenerator)
    • Reflection path (TUnit.Engine)
  • Snapshot Tests: If I changed source generator output or public APIs:
    • I ran TUnit.Core.SourceGenerator.Tests and/or TUnit.PublicAPI tests
    • I reviewed the .received.txt files and accepted them as .verified.txt
    • I committed the updated .verified.txt files
  • Performance: If this change affects hot paths (test discovery, execution, assertions):
    • I minimized allocations and avoided LINQ in hot paths
    • I cached reflection results where appropriate
  • AOT Compatibility: If this change uses reflection:
    • I added appropriate [DynamicallyAccessedMembers] annotations
    • I verified the change works with dotnet publish -p:PublishAot=true

Testing

  • All existing tests pass (dotnet test)
  • I have added tests that cover my changes
  • I have tested both source-generated and reflection modes (if applicable)

Additional Notes

@vbreuss vbreuss force-pushed the topic/add-benchmarks-against-sourcegenerated-mocking-frameworks branch from 8ca4bf2 to fdd6cca Compare March 29, 2026 13:38
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Good addition to expand the benchmark coverage to Imposter and Mockolate. The benchmarks cover the full set of scenarios (creation, setup, invocation, callbacks, verification, combined workflow), which is consistent with how the existing frameworks are benchmarked. A few issues worth addressing before merging:


Bug: Inconsistent use of imposter.Instance() in VerificationBenchmarks.cs

In CallbackBenchmarks.cs and CombinedWorkflowBenchmarks.cs, the Imposter benchmarks correctly retrieve the typed interface instance before invoking methods:

var instance = imposter.Instance();
instance.Send("user@test.com", "Hello");

But in VerificationBenchmarks.cs (both Imposter_Verify and Imposter_VerifyMultiple), methods are called directly on imposter without calling .Instance():

var calc = imposter;  // this is the imposter wrapper, not the interface instance
calc.Add(1, 2);       // are these calls actually being recorded?

If ICalculatorServiceImposter doesn't implement ICalculatorService, these calls go to a different code path than what's intended — meaning the verify assertions may be vacuously true (verifying 0 calls = 0 expected) rather than validating that real invocations were counted. The benchmarks would be measuring something different from the other frameworks.


Potential issue: Imposter_CreateMock returns the wrapper, not the instance

public object Imposter_CreateMock()
{
    var imposter = ICalculatorService.Imposter();
    return imposter;  // returns the ICalculatorServiceImposter wrapper
}

All other frameworks return the actual mock object (the ICalculatorService-implementing instance). The Mockolate version also returns the mock object directly since CreateMock() returns the mock implementing the interface. For a fair comparison, Imposter should probably return imposter.Instance() here as well.


Potential duplicate [assembly: GenerateImposter] attributes

The same type is registered with [assembly: GenerateImposter] in multiple files:

  • ICalculatorService appears in MockCreationBenchmarks.cs, SetupBenchmarks.cs, InvocationBenchmarks.cs, and VerificationBenchmarks.cs
  • IUserRepository appears in MockCreationBenchmarks.cs, SetupBenchmarks.cs, CombinedWorkflowBenchmarks.cs, and VerificationBenchmarks.cs

If GenerateImposterAttribute has AllowMultiple = true, this compiles but may cause the source generator to emit duplicate code (duplicate class definitions). If it has AllowMultiple = false, this is a compile error. These should be consolidated into a single location (e.g., a dedicated ImposterRegistrations.cs file, similar to how the existing code groups them), with one [assembly: GenerateImposter] per type.


Minor: Mixed indentation in Directory.Packages.props

The Imposter entry uses a tab while Mockolate uses spaces, which is inconsistent with the rest of the file.


Summary

The overall structure is solid and consistent with the existing benchmark patterns. The main concern is the correctness of the Imposter verification benchmarks — if methods are being called on the wrapper instead of the interface instance, those benchmarks don't measure what they appear to measure. Please verify the VerificationBenchmarks Imposter code path produces correct call-count tracking before merging.

@vbreuss vbreuss force-pushed the topic/add-benchmarks-against-sourcegenerated-mocking-frameworks branch 2 times, most recently from 7621651 to c244de4 Compare March 29, 2026 13:52
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Code Review

Comparing against my previous review on the earlier commit — two of the four issues have been addressed:

✅ Fixed: Imposter_CreateMock now returns imposter.Instance()

✅ Fixed: VerificationBenchmarks now correctly calls imposter.Instance() before invoking methods


Still present: Duplicate [assembly: GenerateImposter] attributes

The same types are still declared in multiple files:

  • ICalculatorService: MockCreationBenchmarks.cs, SetupBenchmarks.cs, InvocationBenchmarks.cs, VerificationBenchmarks.cs
  • IUserRepository: MockCreationBenchmarks.cs, SetupBenchmarks.cs, CombinedWorkflowBenchmarks.cs, VerificationBenchmarks.cs
  • ILogger: CallbackBenchmarks.cs, CombinedWorkflowBenchmarks.cs

This compiles only if GenerateImposterAttribute has [AttributeUsage(AllowMultiple = true)]. Even if it does compile, the source generator may emit duplicate class definitions. The fix is to consolidate all registrations into a single file (e.g. ImposterRegistrations.cs), one attribute per type — same pattern that's cleanest for InternalsVisibleTo and similar assembly attributes.


New: Wrong matcher type in Imposter_MultipleSetups (SetupBenchmarks.cs)

// SetupBenchmarks.cs — uses Moq.It (aliased as It) with Imposter
imposter.Exists(It.IsAny<int>()).Returns(true);  // ❌ Moq.It, not Imposter's Arg<T>

All other Imposter setups in this PR use Arg<int>.Any(), which is Imposter's own matcher API:

imposter.Add(Arg<int>.Any(), Arg<int>.Any()).Returns(42);  // ✅ consistent

This is likely a copy-paste oversight from the Mockolate or Moq setups. If Imposter's source-generated proxy doesn't accept Moq expression matchers, the setup will either silently fail to match (returning default values) or throw at runtime — either way producing misleading benchmark results. This should be:

imposter.Exists(Arg<int>.Any()).Returns(true);

Summary

Two meaningful fixes landed in this update — good progress. The remaining blocker is the potential mismatched matcher in Imposter_MultipleSetups, which could cause the setup to silently not match and benchmark incorrect behavior. The duplicate [assembly: GenerateImposter] attributes are a structural concern worth cleaning up but less likely to cause runtime issues if the attribute allows multiples.

@vbreuss vbreuss force-pushed the topic/add-benchmarks-against-sourcegenerated-mocking-frameworks branch 2 times, most recently from 62711ac to 2263209 Compare March 29, 2026 15:35
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Add benchmarks for Imposter and Mockolate

Thanks for expanding the benchmark suite — having more source-generated mocking frameworks in the comparison is genuinely useful. The benchmark code itself is correct and follows the existing patterns well. A few things worth raising:


Duplicate attributes

The attribute for the same types is declared in multiple files. For example, appears in both and , and appears in both and , etc.

Assembly-level attributes with the same type argument will cause a compile error if they truly duplicate. At minimum this is confusing — they should be consolidated into a single file (e.g., a dedicated or alongside other globals in ). The fact that the PR currently references five separate files each declaring for overlapping types is a red flag that this may not compile cleanly.


Asymmetric benchmark design in

For , the benchmark returns (the mock wrapper object itself), while for Imposter and all other frameworks the benchmark returns the actual interface implementation (, , etc.).

If the creation cost of the proxy is amortized into the wrapper or deferred to first use, returning the wrapper instead of the proxy could make Mockolate appear artificially faster in the creation benchmark. This should be returning the actual usable object for a fair comparison.


Inconsistency in setup between Imposter and Mockolate

In , all other frameworks (TUnit.Mocks, Moq, NSubstitute, FakeItEasy) store both the mock handle and the proxied object as fields — which is the correct pattern for invocation-only benchmarks where setup overhead should be excluded via . The Imposter fields are added correctly, but is typed as and the setup calls — it's worth confirming whether actually returns something assignable to or whether it returns a wrapper that implements the interface. If it's a wrapper (same issue as above), then in the benchmark may be going through additional dispatch that the others don't.


Missing in several files

The diff removes from , , , and , replacing it with . However, the , , etc. types are still used in those files. This works because of the namespace alias, but still requires either or fully qualified names. This may already work (since is fully qualified), but it's worth double-checking that no (unqualified) usages exist that would now ambiguously resolve to from the global using.

Specifically in :

Using (Moq's matcher) in an Imposter verify call may not match correctly — Imposter uses its own matcher. If Imposter's verification doesn't understand Moq's , these verifications could silently pass or fail incorrectly, producing misleading benchmark output.


Minor: in setup benchmarks

In and , is used. The existing TUnit.Mocks, Moq, NSubstitute, and FakeItEasy variants also use , so this is consistent, but it's a minor GC allocation that happens to be identical across all implementations, so it's fine from a fairness standpoint.


Summary

The most impactful issues to address before merging:

  1. Consolidate declarations to avoid potential duplicate attribute issues.
  2. Verify returns the proxied instance, not the wrapper, for a fair comparison.
  3. Check that (Moq) used in Imposter verification calls actually works correctly with Imposter's argument matching — if not, use consistently.

Overall the contribution is solid and the benchmark coverage is a welcome addition to the suite.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Add benchmarks for Imposter and Mockolate

Thanks for expanding the benchmark suite — having more source-generated mocking frameworks in the comparison is genuinely useful. The benchmark code itself is correct and follows the existing patterns well. A few things worth raising:


Duplicate [assembly: GenerateImposter(...)] attributes

The GenerateImposter attribute for the same types is declared in multiple files. For example, [assembly: GenerateImposter(typeof(TUnit.Mocks.Benchmarks.ICalculatorService))] appears in both MockCreationBenchmarks.cs and SetupBenchmarks.cs, and ILogger appears in both CallbackBenchmarks.cs and CombinedWorkflowBenchmarks.cs.

Assembly-level attributes with the same type argument will cause a compile error if they truly duplicate. At minimum this is confusing — they should be consolidated into a single file (e.g., a dedicated ImposterAttributes.cs or alongside other globals in GlobalUsings.cs). Having five separate files each declaring [assembly: GenerateImposter] for overlapping types is a red flag that this may not compile cleanly.


Asymmetric benchmark design in MockCreationBenchmarks

For Mockolate_CreateMock, the benchmark returns sut (the mock wrapper object itself), while for Imposter and all other frameworks the benchmark returns the actual interface implementation (.Instance(), .Object, etc.).

If the creation cost of the proxy is amortized into the wrapper or deferred to first use, returning the wrapper instead of the proxy could make Mockolate appear artificially faster in the creation benchmark. The benchmark should return the actual usable proxied instance for a fair comparison.


Potential matcher mismatch in Imposter verification

In CombinedWorkflowBenchmarks.Imposter_FullWorkflow, Moq's It.IsAny<T>() matcher is used inside Imposter's Called() verification calls:

repoImposter.Save(It.IsAny<User>()).Called(Count.Once());
loggerImposter.Log(It.IsAny<string>(), It.IsAny<string>()).Called(Count.Once());

Imposter uses its own Arg<T>.Any() argument matchers (as used consistently throughout the rest of the Imposter benchmarks). If Imposter's verification doesn't understand Moq's It.IsAny<T>(), these verifications could silently pass regardless of actual invocations, producing misleading benchmark results. These should use Arg<T>.Any() for consistency.


Removing using Moq — potential ambiguity

The diff removes using Moq; from several files, replacing it with using It = Moq.It;. However, GlobalUsings.cs already has global using TUnit.Mocks;, which brings Mock<T> into scope as TUnit.Mocks.Mock<T>. Worth a quick scan to ensure no unqualified Moq types were inadvertently re-routed as a result of this change.


Minor: InvocationBenchmarks Mockolate field type

In InvocationBenchmarks, the Mockolate mock is stored as ICalculatorService? and used directly (rather than via a .Object or .Instance() accessor like the other frameworks). This is fine if CreateMock() returns a type implementing ICalculatorService directly, but it's worth confirming there isn't hidden wrapper dispatch happening during the invocation benchmarks.


Summary

Most impactful issues to address before merging:

  1. Consolidate [assembly: GenerateImposter(...)] declarations into one file to avoid potential duplicate attribute compile errors.
  2. Verify Mockolate_CreateMock returns the proxied instance (not the wrapper) for a fair comparison with other frameworks.
  3. Replace It.IsAny<T>() with Arg<T>.Any() in Imposter verification calls in CombinedWorkflowBenchmarks to avoid potential silent false-pass verification.

Overall this is a welcome addition to the benchmark coverage.

@vbreuss vbreuss force-pushed the topic/add-benchmarks-against-sourcegenerated-mocking-frameworks branch from 2263209 to 0459cf8 Compare March 29, 2026 15:59
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Code Review

Comparing against the previous reviews — several issues have been resolved, but two concerns remain.


✅ Fixed: VerificationBenchmarks correctly calls .Instance() before invoking methods

✅ Fixed: Imposter_CreateMock now returns imposter.Instance()

✅ Fixed: CombinedWorkflowBenchmarks.Imposter_FullWorkflow uses Arg<T>.Any() consistently for Imposter verification


Still present: Duplicate [assembly: GenerateImposter(...)] attributes

The same types are still declared across multiple files:

  • ICalculatorService: MockCreationBenchmarks.cs, SetupBenchmarks.cs, InvocationBenchmarks.cs, VerificationBenchmarks.cs
  • IUserRepository: MockCreationBenchmarks.cs, SetupBenchmarks.cs, CombinedWorkflowBenchmarks.cs, VerificationBenchmarks.cs
  • ILogger: CallbackBenchmarks.cs, CombinedWorkflowBenchmarks.cs
  • INotificationService: CallbackBenchmarks.cs

This has been flagged in every prior review. If GenerateImposterAttribute has [AttributeUsage(AllowMultiple = true)], it compiles — but the source generator may still emit duplicate class definitions for the same interface, causing build errors. The correct architectural fix is to consolidate all of these into a single file (e.g., ImposterRegistrations.cs), exactly as you'd consolidate [assembly: InternalsVisibleTo] attributes. Each interface should appear exactly once.


Still present: Wrong matcher in Mockolate_MultipleSetups (SetupBenchmarks.cs)

The file has using It = Moq.It;, so It.IsAny<int>() is Moq.It.IsAny<int>(). All other Mockolate setups in this file and the rest of the PR use Mockolate.It.IsAny<T>() (fully qualified):

This was flagged in the previous review. If Mockolate's source-generated proxy uses runtime matcher inspection (not compile-time code generation), then passing Moq.It.IsAny<int>() into a Mockolate setup will silently fail to match — the mock returns the default value instead of true, and the benchmark measures a partially-broken configuration. Fix:


Summary

Three rounds of fixes have landed — good progress, and the overall benchmark structure is solid. The two remaining issues are both carryovers from earlier reviews:

  1. Consolidate [assembly: GenerateImposter(...)] into one file — avoids potential duplicate code generation.
  2. Fix Mockolate.It vs Moq.It in Mockolate_MultipleSetups — ensures the setup actually registers the return value and the benchmark measures correct behavior.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Code Review

Comparing against the previous reviews — several issues have been resolved, but two concerns remain.


✅ Fixed: VerificationBenchmarks correctly calls .Instance() before invoking methods

✅ Fixed: Imposter_CreateMock now returns imposter.Instance()

✅ Fixed: CombinedWorkflowBenchmarks.Imposter_FullWorkflow uses Arg<T>.Any() consistently for Imposter verification


Still present: Duplicate [assembly: GenerateImposter(...)] attributes

The same types are still declared across multiple files:

  • ICalculatorService: MockCreationBenchmarks.cs, SetupBenchmarks.cs, InvocationBenchmarks.cs, VerificationBenchmarks.cs
  • IUserRepository: MockCreationBenchmarks.cs, SetupBenchmarks.cs, CombinedWorkflowBenchmarks.cs, VerificationBenchmarks.cs
  • ILogger: CallbackBenchmarks.cs, CombinedWorkflowBenchmarks.cs
  • INotificationService: CallbackBenchmarks.cs

This has been flagged in every prior review. If GenerateImposterAttribute has [AttributeUsage(AllowMultiple = true)], it compiles — but the source generator may still emit duplicate class definitions for the same interface, causing build errors. The correct fix is to consolidate all of these into a single file (e.g., ImposterRegistrations.cs), exactly as you'd consolidate [assembly: InternalsVisibleTo] attributes. Each interface should appear exactly once.


Still present: Wrong matcher in Mockolate_MultipleSetups (SetupBenchmarks.cs)

The file has using It = Moq.It;, so unqualified It.IsAny<int>() resolves to Moq.It.IsAny<int>(). All other Mockolate setups in this PR use fully-qualified Mockolate.It.IsAny<T>(), but this line uses the alias:

sut.Mock.Setup.Exists(It.IsAny<int>()).Returns(true);  // ❌ Moq.It, not Mockolate.It

If Mockolate's runtime matcher inspection doesn't recognize Moq.It.IsAny<int>(), this setup silently fails to match — the mock returns the default value instead of true. The fix is straightforward:

sut.Mock.Setup.Exists(Mockolate.It.IsAny<int>()).Returns(true);

Summary

Three rounds of fixes have landed — good progress, and the overall benchmark structure is solid. The two remaining issues are both carryovers from earlier reviews:

  1. Consolidate [assembly: GenerateImposter(...)] into one file — avoids potential duplicate code generation.
  2. Fix Mockolate.It vs Moq.It in Mockolate_MultipleSetups — ensures the setup registers the return value correctly.

@vbreuss vbreuss force-pushed the topic/add-benchmarks-against-sourcegenerated-mocking-frameworks branch from 0459cf8 to 707e849 Compare March 29, 2026 16:42
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Add benchmarks for Imposter and Mockolate mocking frameworks

Overall this is a solid, clean addition. The benchmarks follow the existing patterns well and cover all the same scenarios as the other frameworks. A few observations and suggestions:

Potential benchmark validity concern: Imposter_VerifyNever

In VerificationBenchmarks.cs, the Imposter "Never" benchmark does not instantiate the mock (no .Instance() call) before verifying, which may not fully match the other frameworks' patterns that instantiate the mock even if they never call it:

[Benchmark(Description = "Imposter (Never)")]
public void Imposter_VerifyNever()
{
    var imposter = ICalculatorService.Imposter();
    imposter.Format(Arg<int>.Any()).Called(Count.Never());
    // No: var calc = imposter.Instance();
}

For fair comparison, check whether instantiation is required for verification and whether the other frameworks' "Never" benchmarks do more work (e.g., Moq creates the mock but also calls mock.Verify(...) without instantiating .Object). If Imposter requires instantiation to track calls, this could report misleadingly fast results.

Inconsistency: Mockolate_CreateMock returns the mock wrapper, not the underlying object

In MockCreationBenchmarks.cs, most benchmarks return the usable object:

// Imposter - returns the instance (the callable object)
public object Imposter_CreateMock()
{
    var imposter = ICalculatorService.Imposter();
    return imposter.Instance();
}

// Mockolate - returns the mock wrapper directly (includes Mock property etc.)
public object Mockolate_CreateMock()
{
    var sut = ICalculatorService.CreateMock();
    return sut;  // This is a wrapper type, not ICalculatorService
}

Since ICalculatorService.CreateMock() returns a type that implements ICalculatorService plus a Mock property, returning it directly may be fine — but it's worth confirming whether the returned object is the interface implementation itself or an additional wrapper, so the benchmark measures the same conceptual operation as the other frameworks.

new List<User>() allocations in setup benchmarks

In SetupBenchmarks.Mockolate_MultipleSetups and Imposter_MultipleSetups, new List<User>() is allocated inside the benchmark method (matching the pattern of the other frameworks). This is consistent, but worth noting since BenchmarkDotNet with [MemoryDiagnoser] will attribute those allocations to the mock framework being measured. All frameworks do this equivalently, so comparison is fair — just something to be aware of when reading results.

Minor: ImposterRegistrations.cs placement

The new ImposterRegistrations.cs file uses [assembly: GenerateImposter(...)] attributes which is the correct source-generator approach for Imposter. This is clean and follows AOT-compatible patterns. No issues here.

Qualified Moq.It references

The PR correctly qualifies the existing It.IsAny<T>() calls to Moq.It.IsAny<T>() to resolve ambiguity with the new Mockolate.It type. These are necessary fixups and look correct.

Summary

The implementation is straightforward and consistent with the existing benchmark structure. The main thing worth double-checking is the Imposter_VerifyNever scenario to ensure it's measuring a comparable operation to the other "Never" verification benchmarks. Everything else looks good.

This was referenced Mar 30, 2026
intellitect-bot pushed a commit to IntelliTect/EssentialCSharp.Web that referenced this pull request Mar 30, 2026
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.21.30 to
1.23.7.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.23.7

<!-- Release notes generated using configuration in .github/release.yml
at v1.23.7 -->

## What's Changed
### Other Changes
* feat: use results directory provided by Microsoft Testing Platform in
HtmlReporter by @​DavidZidar in
thomhurst/TUnit#5294
* feat: add benchmarks for Imposter and Mockolate mocking frameworks by
@​vbreuss in thomhurst/TUnit#5295
* feat: add TUnit0080 analyzer for missing polyfill types by @​thomhurst
in thomhurst/TUnit#5292
* fix: respect user-set TUnitImplicitUsings from Directory.Build.props
by @​thomhurst in thomhurst/TUnit#5280
* perf: optimize TUnit.Mocks hot paths by @​thomhurst in
thomhurst/TUnit#5300
### Dependencies
* chore(deps): update tunit to 1.22.19 by @​thomhurst in
thomhurst/TUnit#5296

## New Contributors
* @​DavidZidar made their first contribution in
thomhurst/TUnit#5294

**Full Changelog**:
thomhurst/TUnit@v1.22.19...v1.23.7

## 1.22.19

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.19 -->

## What's Changed
### Other Changes
* Add mock library benchmarks: TUnit.Mocks vs Moq, NSubstitute,
FakeItEasy by @​Copilot in thomhurst/TUnit#5284
* perf: lazily initialize optional MockEngine collections by @​thomhurst
in thomhurst/TUnit#5289
* Always emit TUnit.Mocks.Generated namespace from source generator by
@​Copilot in thomhurst/TUnit#5282
### Dependencies
* chore(deps): update tunit to 1.22.6 by @​thomhurst in
thomhurst/TUnit#5285


**Full Changelog**:
thomhurst/TUnit@v1.22.6...v1.22.19

## 1.22.6

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.6 -->

## What's Changed
### Other Changes
* fix: use IComputeResource to filter waitable Aspire resources by
@​thomhurst in thomhurst/TUnit#5278
* fix: preserve StateBag when creating per-test TestBuilderContext by
@​thomhurst in thomhurst/TUnit#5279
### Dependencies
* chore(deps): update tunit to 1.22.3 by @​thomhurst in
thomhurst/TUnit#5275


**Full Changelog**:
thomhurst/TUnit@v1.22.3...v1.22.6

## 1.22.3

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.3 -->

## What's Changed
### Other Changes
* fix: pass assembly version properties to dotnet pack by @​thomhurst in
thomhurst/TUnit#5274
### Dependencies
* chore(deps): update tunit to 1.22.0 by @​thomhurst in
thomhurst/TUnit#5272


**Full Changelog**:
thomhurst/TUnit@v1.22.0...v1.22.3

## 1.22.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.0 -->

## What's Changed
### Other Changes
* perf: run GitVersion once in CI instead of per-project by @​slang25 in
thomhurst/TUnit#5259
* perf: disable GitVersion MSBuild task globally by @​thomhurst in
thomhurst/TUnit#5266
* fix: skip IResourceWithoutLifetime resources in Aspire fixture wait
logic by @​thomhurst in thomhurst/TUnit#5268
* fix: relax docs site Node.js engine constraint to >=24 by @​thomhurst
in thomhurst/TUnit#5269
* fix: catch unhandled exceptions in ExecuteRequestAsync to prevent IDE
RPC crashes by @​thomhurst in
thomhurst/TUnit#5271
* feat: register HTML report as MTP session artifact by @​thomhurst in
thomhurst/TUnit#5270
### Dependencies
* chore(deps): update tunit to 1.21.30 by @​thomhurst in
thomhurst/TUnit#5254
* chore(deps): update opentelemetry to 1.15.1 by @​thomhurst in
thomhurst/TUnit#5258
* chore(deps): bump node-forge from 1.3.1 to 1.4.0 in /docs by
@​dependabot[bot] in thomhurst/TUnit#5255
* chore(deps): bump picomatch from 2.3.1 to 2.3.2 in /docs by
@​dependabot[bot] in thomhurst/TUnit#5256
* chore(deps): update react by @​thomhurst in
thomhurst/TUnit#5261
* chore(deps): update node.js to >=18.20.8 by @​thomhurst in
thomhurst/TUnit#5262
* chore(deps): update node.js to v24 by @​thomhurst in
thomhurst/TUnit#5264


**Full Changelog**:
thomhurst/TUnit@v1.21.30...v1.22.0

Commits viewable in [compare
view](thomhurst/TUnit@v1.21.30...v1.23.7).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit&package-manager=nuget&previous-version=1.21.30&new-version=1.23.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
github-actions bot pushed a commit to IntelliTect/CodingGuidelines that referenced this pull request Mar 30, 2026
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.21.6 to
1.23.7.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit.Core's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.23.7

<!-- Release notes generated using configuration in .github/release.yml
at v1.23.7 -->

## What's Changed
### Other Changes
* feat: use results directory provided by Microsoft Testing Platform in
HtmlReporter by @​DavidZidar in
thomhurst/TUnit#5294
* feat: add benchmarks for Imposter and Mockolate mocking frameworks by
@​vbreuss in thomhurst/TUnit#5295
* feat: add TUnit0080 analyzer for missing polyfill types by @​thomhurst
in thomhurst/TUnit#5292
* fix: respect user-set TUnitImplicitUsings from Directory.Build.props
by @​thomhurst in thomhurst/TUnit#5280
* perf: optimize TUnit.Mocks hot paths by @​thomhurst in
thomhurst/TUnit#5300
### Dependencies
* chore(deps): update tunit to 1.22.19 by @​thomhurst in
thomhurst/TUnit#5296

## New Contributors
* @​DavidZidar made their first contribution in
thomhurst/TUnit#5294

**Full Changelog**:
thomhurst/TUnit@v1.22.19...v1.23.7

## 1.22.19

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.19 -->

## What's Changed
### Other Changes
* Add mock library benchmarks: TUnit.Mocks vs Moq, NSubstitute,
FakeItEasy by @​Copilot in thomhurst/TUnit#5284
* perf: lazily initialize optional MockEngine collections by @​thomhurst
in thomhurst/TUnit#5289
* Always emit TUnit.Mocks.Generated namespace from source generator by
@​Copilot in thomhurst/TUnit#5282
### Dependencies
* chore(deps): update tunit to 1.22.6 by @​thomhurst in
thomhurst/TUnit#5285


**Full Changelog**:
thomhurst/TUnit@v1.22.6...v1.22.19

## 1.22.6

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.6 -->

## What's Changed
### Other Changes
* fix: use IComputeResource to filter waitable Aspire resources by
@​thomhurst in thomhurst/TUnit#5278
* fix: preserve StateBag when creating per-test TestBuilderContext by
@​thomhurst in thomhurst/TUnit#5279
### Dependencies
* chore(deps): update tunit to 1.22.3 by @​thomhurst in
thomhurst/TUnit#5275


**Full Changelog**:
thomhurst/TUnit@v1.22.3...v1.22.6

## 1.22.3

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.3 -->

## What's Changed
### Other Changes
* fix: pass assembly version properties to dotnet pack by @​thomhurst in
thomhurst/TUnit#5274
### Dependencies
* chore(deps): update tunit to 1.22.0 by @​thomhurst in
thomhurst/TUnit#5272


**Full Changelog**:
thomhurst/TUnit@v1.22.0...v1.22.3

## 1.22.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.22.0 -->

## What's Changed
### Other Changes
* perf: run GitVersion once in CI instead of per-project by @​slang25 in
thomhurst/TUnit#5259
* perf: disable GitVersion MSBuild task globally by @​thomhurst in
thomhurst/TUnit#5266
* fix: skip IResourceWithoutLifetime resources in Aspire fixture wait
logic by @​thomhurst in thomhurst/TUnit#5268
* fix: relax docs site Node.js engine constraint to >=24 by @​thomhurst
in thomhurst/TUnit#5269
* fix: catch unhandled exceptions in ExecuteRequestAsync to prevent IDE
RPC crashes by @​thomhurst in
thomhurst/TUnit#5271
* feat: register HTML report as MTP session artifact by @​thomhurst in
thomhurst/TUnit#5270
### Dependencies
* chore(deps): update tunit to 1.21.30 by @​thomhurst in
thomhurst/TUnit#5254
* chore(deps): update opentelemetry to 1.15.1 by @​thomhurst in
thomhurst/TUnit#5258
* chore(deps): bump node-forge from 1.3.1 to 1.4.0 in /docs by
@​dependabot[bot] in thomhurst/TUnit#5255
* chore(deps): bump picomatch from 2.3.1 to 2.3.2 in /docs by
@​dependabot[bot] in thomhurst/TUnit#5256
* chore(deps): update react by @​thomhurst in
thomhurst/TUnit#5261
* chore(deps): update node.js to >=18.20.8 by @​thomhurst in
thomhurst/TUnit#5262
* chore(deps): update node.js to v24 by @​thomhurst in
thomhurst/TUnit#5264


**Full Changelog**:
thomhurst/TUnit@v1.21.30...v1.22.0

## 1.21.30

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.30 -->

## What's Changed
### Other Changes
* feat: add test discovery Activity span for tracing by @​thomhurst in
thomhurst/TUnit#5246
* Fix mock generator not preserving nullable annotations on reference
types by @​Copilot in thomhurst/TUnit#5251
* Fix ITestSkippedEventReceiver not firing for [Skip]-attributed tests
by @​thomhurst in thomhurst/TUnit#5253
* Use CallerArgumentExpression for TestDataRow by default. by @​m-gasser
in thomhurst/TUnit#5135
### Dependencies
* chore(deps): update tunit to 1.21.24 by @​thomhurst in
thomhurst/TUnit#5247


**Full Changelog**:
thomhurst/TUnit@v1.21.24...v1.21.30

## 1.21.24

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.24 -->

## What's Changed
### Other Changes
* Fix OpenTelemetry missing root span by reordering session activity
lifecycle by @​Copilot in thomhurst/TUnit#5245
### Dependencies
* chore(deps): update tunit to 1.21.20 by @​thomhurst in
thomhurst/TUnit#5241
* chore(deps): update dependency stackexchange.redis to 2.12.8 by
@​thomhurst in thomhurst/TUnit#5243


**Full Changelog**:
thomhurst/TUnit@v1.21.20...v1.21.24

## 1.21.20

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.20 -->

## What's Changed
### Other Changes
* fix: respect TUnitImplicitUsings set in Directory.Build.props by
@​thomhurst in thomhurst/TUnit#5225
* feat: covariant assertions for interfaces and non-sealed classes by
@​thomhurst in thomhurst/TUnit#5226
* feat: support string-to-parseable type conversions in [Arguments] by
@​thomhurst in thomhurst/TUnit#5227
* feat: add string length range assertions by @​thomhurst in
thomhurst/TUnit#4935
* Fix BeforeEvery/AfterEvery hooks for Class and Assembly not being
executed by @​Copilot in thomhurst/TUnit#5239
### Dependencies
* chore(deps): update tunit to 1.21.6 by @​thomhurst in
thomhurst/TUnit#5228
* chore(deps): update dependency gitversion.msbuild to 6.7.0 by
@​thomhurst in thomhurst/TUnit#5229
* chore(deps): update dependency gitversion.tool to v6.7.0 by
@​thomhurst in thomhurst/TUnit#5230
* chore(deps): update aspire to 13.2.0 - autoclosed by @​thomhurst in
thomhurst/TUnit#5232
* chore(deps): update dependency typescript to v6 by @​thomhurst in
thomhurst/TUnit#5233
* chore(deps): update dependency polyfill to 9.23.0 by @​thomhurst in
thomhurst/TUnit#5235
* chore(deps): update dependency polyfill to 9.23.0 by @​thomhurst in
thomhurst/TUnit#5236


**Full Changelog**:
thomhurst/TUnit@v1.21.6...v1.21.20

Commits viewable in [compare
view](thomhurst/TUnit@v1.21.6...v1.23.7).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit.Core&package-manager=nuget&previous-version=1.21.6&new-version=1.23.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants