fix(core): fill optional params when invoking MethodDataSource via reflection#5880
Conversation
…flection Reflection MethodInfo.Invoke does not auto-fill default-valued parameters the way a C# call site does. The source-gen Factory path emits a direct C# call so the compiler injects defaults, but the reflection fallback — taken for [InheritsTests] derived classes whose data-source method lives on a generic abstract base — passed the user-supplied Arguments array verbatim, throwing TargetParameterCountException whenever the data source declared an optional parameter (e.g. [EnumeratorCancellation] CancellationToken). Add BuildInvokeArgs to pad the args array to the method's parameter count, preferring ParameterInfo.DefaultValue, then CancellationToken.None for unannotated CT params, then falling back to the original array so genuine count mismatches still surface their natural error. Closes #5879
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 6 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Code Review
The fix correctly identifies and addresses the root cause: MethodInfo.Invoke does not auto-fill optional parameters the way a C# call site does, and the reflection fallback path for generic abstract base + [InheritsTests] wasn't accounting for this.
Regression: Truncating Extra Arguments
There is a subtle behavioural regression in BuildInvokeArgs when suppliedArguments.Length > parameters.Length:
// current code — enters the loop even when too many args are supplied
var args = new object?[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
if (i < suppliedArguments.Length)
args[i] = suppliedArguments[i]; // fills params.Length slots from supplied
// ...
}
return args; // silently drops any extra supplied argumentsBefore the fix: methodInfo.Invoke(instance, suppliedArguments) with extra args → TargetParameterCountException (informative, expected).
After the fix: extra args are silently truncated and Invoke succeeds, masking a misconfigured [MethodDataSource] attribute.
Adding a guard before entering the loop restores the original behaviour for this case:
if (suppliedArguments.Length > parameters.Length)
return suppliedArguments; // let Invoke surface the mismatchSuggestion: Use Type.Missing Instead of Manual Default Filling
The current approach special-cases HasDefaultValue, then separately special-cases CancellationToken. A simpler, more general solution is to use Type.Missing — specifically designed for this scenario in reflection:
private static object?[] BuildInvokeArgs(MethodInfo methodInfo, object?[] suppliedArguments)
{
var parameters = methodInfo.GetParameters();
if (parameters.Length == suppliedArguments.Length)
return suppliedArguments;
if (suppliedArguments.Length > parameters.Length)
return suppliedArguments; // let Invoke surface the count mismatch
var args = new object?[parameters.Length];
suppliedArguments.CopyTo(args, 0);
for (var i = suppliedArguments.Length; i < parameters.Length; i++)
args[i] = Type.Missing;
return args;
}Why this is better:
Type.Missingis the .NET-documented idiom for "use the parameter's default value" in reflection — it works for all optional param types without enumerating them.- Eliminates the
CancellationToken-specific branch, so the method stays correct if future data-source methods have other optional parameter types (e.g.,bool flag = true,int count = 0). - For required parameters (no default),
InvokethrowsMissingMemberExceptionrather than the less-informativeTargetParameterCountException, giving clearer diagnostic output. - Supported on net8+ (all TUnit targets) —
Type.Missingfor non-COM optional params has worked since .NET Framework 4.0.
What's Good
- Root cause diagnosis in the PR description is accurate and clearly written.
- The
HasDefaultValue→CancellationToken.Nonefallback chain is correct for the reported scenario. - The regression test in
TUnit.TestProject/Bugs/5879/Repro.csis a clean minimal repro using the exact failing pattern —[InheritsTests], generic abstract base,[EnumeratorCancellation] CancellationToken ct = default. - The comment in the method explains why the helper exists (reflection vs compiler behaviour), which is the right kind of comment.
Summary
The fix works for the reported bug. The two items worth addressing before merge are the silent truncation regression (should be a one-liner guard) and considering Type.Missing to avoid the growing list of type-specific special cases as this code evolves.
|
Thanks for the review. Truncation regression — applied in af2d29d. Changed the early-return guard from
|
There was a problem hiding this comment.
Code Review — Follow-up
Previous Review Items
✅ Behavioral regression (surplus args) — addressed in af2d29d3
The updated guard if (parameters.Length <= suppliedArguments.Length) correctly handles both exact-match and surplus-arg cases by returning suppliedArguments unchanged, letting Invoke surface the TargetParameterCountException. Good fix.
Type.Missing suggestion — partially applicable, but current code is defensible
The previous review suggested replacing the manual default-filling with Type.Missing. On reflection (pun intended), there is a scenario where Type.Missing would not be a full replacement:
Type.Missingonly works for optional parameters that have a= defaultValuein the signature (HasDefaultValue == true).- For a
CancellationTokenparameter without a default value (e.g.,[EnumeratorCancellation] CancellationToken ct),Type.Missingwould causeInvoketo throw rather than usingCancellationToken.None.
The current HasDefaultValue → CancellationToken.None chain does handle that edge case. However, the CancellationToken branch is only reachable when HasDefaultValue == false, which is not the canonical [EnumeratorCancellation] CancellationToken ct = default pattern from the issue. If a data source method has a required CancellationToken parameter, that feels like a misuse of the API. Consider whether silently injecting CancellationToken.None is the right response there, versus surfacing an actionable error.
A middle ground worth considering:
private static object?[] BuildInvokeArgs(MethodInfo methodInfo, object?[] suppliedArguments)
{
var parameters = methodInfo.GetParameters();
if (parameters.Length <= suppliedArguments.Length)
return suppliedArguments;
var args = new object?[parameters.Length];
Array.Copy(suppliedArguments, args, suppliedArguments.Length);
for (var i = suppliedArguments.Length; i < parameters.Length; i++)
{
var p = parameters[i];
if (p.HasDefaultValue)
args[i] = Type.Missing; // .NET fills the declared default
else if (p.ParameterType == typeof(CancellationToken))
args[i] = CancellationToken.None; // pragmatic fallback for enumerator tokens
else
return suppliedArguments; // required param missing — let Invoke throw
}
return args;
}This uses Type.Missing for the common case (cleaner, works for all optional types without enumeration) while keeping the CancellationToken fallback as an explicit safety net. The Array.Copy + loop-from-offset avoids re-copying already-supplied args.
Overall
The fix is correct for the reported bug. The regression concern from the previous review was properly addressed. The code is in a mergeable state — the remaining suggestion is a simplification, not a correctness issue.
Approve pending the Type.Missing / loop refactor consideration (optional, not blocking).
Per @claude follow-up review on PR #5880. Functionally identical to the previous slot-by-slot fill, but: (a) only iterates the missing tail rather than every parameter, (b) Type.Missing is the documented .NET idiom for 'reflection-fill the declared default'. CancellationToken fallback kept as the explicit safety net for [Optional]-without-[DefaultParameterValue].
|
Applied the |
There was a problem hiding this comment.
Code Review — Final Sign-off
Previous Review Items — All Addressed
✅ Behavioral regression (surplus args) — Fixed in af2d29d. The guard if (parameters.Length <= suppliedArguments.Length) correctly passes both exact-match and surplus-arg arrays through to Invoke unchanged, preserving the original TargetParameterCountException behaviour.
✅ Type.Missing + Array.Copy refactor — Applied in 5833192. The final implementation is exactly the middle ground suggested in the previous review:
if (p.HasDefaultValue)
args[i] = Type.Missing; // .NET fills the declared default (works for all optional types)
else if (p.ParameterType == typeof(CancellationToken))
args[i] = CancellationToken.None; // pragmatic fallback for enumerator tokens without explicit default
else
return suppliedArguments; // required param missing — let Invoke throwThis is the correct design: Type.Missing handles the common case generically (no per-type enumeration), while the explicit CancellationToken branch is a narrow safety net for the [EnumeratorCancellation]-without-= default edge case. The Array.Copy + loop-from-offset avoids re-iterating already-supplied args.
Final State Assessment
The fix is correct, well-scoped, and the implementation is clean:
- Root cause accurately identified and fixed in the reflection invoke path
- No regression introduced for surplus-arg or exact-arg cases
- Generic handling via
Type.Missingwith a targetedCancellationTokensafety net - Regression test covers the exact failing pattern (
[InheritsTests]+ generic abstract base +[EnumeratorCancellation]optional CT) - PR description provides clear rationale for why the source-gen path wasn't affected
Approved. Ready to merge.
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.40.5 to 1.45.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.45.0 <!-- Release notes generated using configuration in .github/release.yml at v1.45.0 --> ## What's Changed ### Other Changes * fix(generator): place CallerArgumentExpression before params in [GenerateAssertion] emit by @JohnVerheij in thomhurst/TUnit#5940 * fix(sourcegen): drop covariant TActual when [GenerateAssertion] method has its own type parameters by @JohnVerheij in thomhurst/TUnit#5935 * feat(assertions): add CancellationToken overload to WaitsFor and Eventually by @JohnVerheij in thomhurst/TUnit#5938 * fix(html-report): extract categories using MTP Key=name convention by @thomhurst in thomhurst/TUnit#5946 * feat(html-report): rewrite as split-pane design template by @thomhurst in thomhurst/TUnit#5947 ### Dependencies * chore(deps): update microsoft.testing to 2.2.3 by @thomhurst in thomhurst/TUnit#5927 * chore(deps): update mstest to 4.2.3 by @thomhurst in thomhurst/TUnit#5928 * chore(deps): update tunit to 1.44.39 by @thomhurst in thomhurst/TUnit#5929 * chore(deps): update aspire to 13.3.3 by @thomhurst in thomhurst/TUnit#5933 * chore(deps): update dependency dompurify to v3.4.4 by @thomhurst in thomhurst/TUnit#5944 * chore(deps): update dependency qs to v6.15.2 by @thomhurst in thomhurst/TUnit#5941 **Full Changelog**: thomhurst/TUnit@v1.44.39...v1.45.0 ## 1.44.39 <!-- Release notes generated using configuration in .github/release.yml at v1.44.39 --> ## What's Changed ### Other Changes * fix(tests): retry trx read to dodge MTP post-exit flush race on Windows by @thomhurst in thomhurst/TUnit#5888 * fix(pipeline): timeout + retry InstallPlaywrightModule so a hung download fails fast by @thomhurst in thomhurst/TUnit#5889 * fix(otel): require two consecutive idle windows in DrainAsync to catch in-transit POSTs by @thomhurst in thomhurst/TUnit#5890 * test(assertions): drop flaky wall-clock upper bound on WaitsFor timeout test by @thomhurst in thomhurst/TUnit#5886 * fix(sourcegen): drop spurious ')' in MethodAssertionGenerator Task<bool> emit by @JohnVerheij in thomhurst/TUnit#5920 * fix(sourcegen): merge generic parameter lists in [AssertionExtension] emit by @JohnVerheij in thomhurst/TUnit#5921 * fix(aspnetcore): scope correlation processor per-factory to stop cross-factory tag leak by @thomhurst in thomhurst/TUnit#5891 * Changed FSharp.Core version to 10.1.300 by @licon4812 in thomhurst/TUnit#5909 * feat(mocks): add Mock.HttpClientFactory() helper by @thomhurst in thomhurst/TUnit#5894 * Harden WaitsFor timeout test by @thomhurst in thomhurst/TUnit#5926 * fix(sourcegen): emit `default` literal for value-type assertion parameters by @JohnVerheij in thomhurst/TUnit#5919 ### Dependencies * chore(deps): update dependency nunit to 4.6.0 by @thomhurst in thomhurst/TUnit#5826 * chore(deps): update tunit to 1.44.0 by @thomhurst in thomhurst/TUnit#5882 * chore(deps): update dependency mockolate to 3.2.0 by @thomhurst in thomhurst/TUnit#5892 * chore(deps): update dependency yaml to v2.9.0 by @thomhurst in thomhurst/TUnit#5887 * chore(deps): update dependency nuget.protocol to 7.6.0 by @thomhurst in thomhurst/TUnit#5897 * chore(deps): update dependency microsoft.entityframeworkcore to 10.0.8 by @thomhurst in thomhurst/TUnit#5898 * chore(deps): update dependency microsoft.templateengine.authoring.cli to v10.0.300 by @thomhurst in thomhurst/TUnit#5899 * chore(deps): update microsoft.extensions by @thomhurst in thomhurst/TUnit#5905 * chore(deps): update microsoft.aspnetcore to 10.0.8 by @thomhurst in thomhurst/TUnit#5904 * chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to 10.0.300 by @thomhurst in thomhurst/TUnit#5902 * chore(deps): update aspire to 13.3.1 by @thomhurst in thomhurst/TUnit#5900 * chore(deps): update dependency system.commandline to 2.0.8 by @thomhurst in thomhurst/TUnit#5903 * chore(deps): update dependency azure.storage.blobs to 12.28.0 by @thomhurst in thomhurst/TUnit#5910 * chore(deps): update dependency dotnet-sdk to v10.0.300 by @thomhurst in thomhurst/TUnit#5901 * chore(deps): update dependency stackexchange.redis to 2.13.1 by @thomhurst in thomhurst/TUnit#5906 * chore(deps): update aspire to 13.3.2 by @thomhurst in thomhurst/TUnit#5924 * chore(deps): bump mermaid from 11.12.2 to 11.15.0 in /docs by @dependabot[bot] in thomhurst/TUnit#5893 * chore(deps): update dependency streamjsonrpc to 2.24.92 by @thomhurst in thomhurst/TUnit#5915 * chore(deps): update dependency dompurify to v3.4.3 by @thomhurst in thomhurst/TUnit#5913 * chore(deps): update microsoft.build to 18.6.3 by @thomhurst in thomhurst/TUnit#5914 **Full Changelog**: thomhurst/TUnit@v1.44.0...v1.44.39 ## 1.44.0 <!-- Release notes generated using configuration in .github/release.yml at v1.44.0 --> ## What's Changed ### Other Changes * Generated mocks live in the same namespace as the mocked type by @thomhurst in thomhurst/TUnit#5870 * Show multi-step test spans in class timeline, align report ordering with execution, and correlate linked OTel activities by @Copilot in thomhurst/TUnit#5847 * fix: don't leak RUC onto Should-style comparer overloads (#5857) by @thomhurst in thomhurst/TUnit#5873 * Fix culture-dependent timestamp in HTML test report (#5868) by @thomhurst in thomhurst/TUnit#5872 * fix(mocks-http): auto-prepend `/` to partial endpoint paths (#5838) by @thomhurst in thomhurst/TUnit#5874 * Replace Report.ExpandClassTimeline with [ClassTimeline] attribute by @thomhurst in thomhurst/TUnit#5875 * feat(assertions): make ShouldAssertion<T> implement IAssertion (#5824) by @thomhurst in thomhurst/TUnit#5876 * feat(mocks): support non-span ref struct out/ref params by @thomhurst in thomhurst/TUnit#5878 * fix(core): fill optional params when invoking MethodDataSource via reflection by @thomhurst in thomhurst/TUnit#5880 * Mocks: structural fix for Mock<T> / mocked-member name collisions by @thomhurst in thomhurst/TUnit#5881 * chore(mocks): promote TUnit.Mocks packages to stable by @thomhurst in thomhurst/TUnit#5877 ### Dependencies * chore(deps): update tunit to 1.43.41 by @thomhurst in thomhurst/TUnit#5863 * chore(deps): update dependency tunit.assertions.fsharp to 1.43.41 by @thomhurst in thomhurst/TUnit#5865 * chore(deps): bump @babel/plugin-transform-modules-systemjs from 7.28.5 to 7.29.4 in /docs by @dependabot[bot] in thomhurst/TUnit#5867 * chore(deps): bump fast-uri from 3.1.0 to 3.1.2 in /docs by @dependabot[bot] in thomhurst/TUnit#5862 **Full Changelog**: thomhurst/TUnit@v1.43.41...v1.44.0 ## 1.43.41 <!-- Release notes generated using configuration in .github/release.yml at v1.43.41 --> ## What's Changed ### Other Changes * feat(playwright): expose default Context/Launch options on settings by @thomhurst in thomhurst/TUnit#5861 * fix(hooks): resolve TestDiscovery hook context type by attribute kind, not method name by @thomhurst in thomhurst/TUnit#5860 ### Dependencies * chore(deps): update tunit to 1.43.38 by @thomhurst in thomhurst/TUnit#5858 **Full Changelog**: thomhurst/TUnit@v1.43.38...v1.43.41 ## 1.43.38 <!-- Release notes generated using configuration in .github/release.yml at v1.43.38 --> ## What's Changed ### Other Changes * feat(playwright): add TUnitPlaywrightSettings defaults by @thomhurst in thomhurst/TUnit#5859 **Full Changelog**: thomhurst/TUnit@v1.43.37...v1.43.38 ## 1.43.37 <!-- Release notes generated using configuration in .github/release.yml at v1.43.37 --> ## What's Changed ### Other Changes * docs: clarify MethodDataSourceAttribute.Factory is source-generator-managed by @Copilot in thomhurst/TUnit#5835 * fix(assertions): skip ref-struct members in IsEquivalentTo (#5841) by @thomhurst in thomhurst/TUnit#5842 * feat(playwright): add composition-based fixtures by @thomhurst in thomhurst/TUnit#5840 ### Dependencies * chore(deps): update tunit to 1.43.11 by @thomhurst in thomhurst/TUnit#5821 * chore(deps): update dependency polyfill to 10.4.0 by @thomhurst in thomhurst/TUnit#5830 * chore(deps): update dependency polyfill to 10.4.0 by @thomhurst in thomhurst/TUnit#5829 * chore(deps): update react to ^19.2.6 by @thomhurst in thomhurst/TUnit#5839 * chore(deps): update dependency polyfill to 10.5.0 by @thomhurst in thomhurst/TUnit#5848 * chore(deps): update dependency polyfill to 10.5.0 by @thomhurst in thomhurst/TUnit#5849 * chore(deps): update aspire to 13.3.0 by @thomhurst in thomhurst/TUnit#5851 * chore(deps): update dependency brace-expansion to v5.0.6 by @thomhurst in thomhurst/TUnit#5853 * chore(deps): update dependency polyfill to 10.5.1 by @thomhurst in thomhurst/TUnit#5854 * chore(deps): update dependency polyfill to 10.5.1 by @thomhurst in thomhurst/TUnit#5855 * chore(deps): update verify to 31.16.3 by @thomhurst in thomhurst/TUnit#5856 **Full Changelog**: thomhurst/TUnit@v1.43.11...v1.43.37 ## 1.43.11 <!-- Release notes generated using configuration in .github/release.yml at v1.43.11 --> ## What's Changed ### Other Changes * perf(engine): skip execution ledger for independent tests by @thomhurst in thomhurst/TUnit#5813 * perf(engine): skip tracked object graph rediscovery by @thomhurst in thomhurst/TUnit#5814 * fix: suppress XML doc warnings in generated sources by @mvanhorn in thomhurst/TUnit#5797 * Fix xUnit Throws migration docs by @thomhurst in thomhurst/TUnit#5819 * Fix Should optional overload generation by @thomhurst in thomhurst/TUnit#5820 ### Dependencies * chore(deps): update dependency mockolate to 3.1.0 by @thomhurst in thomhurst/TUnit#5811 * chore(deps): update tunit to 1.43.2 by @thomhurst in thomhurst/TUnit#5809 * chore(deps): update dependency nunit.analyzers to 4.13.0 by @thomhurst in thomhurst/TUnit#5815 * chore(deps): update dependency yaml to v2.8.4 by @thomhurst in thomhurst/TUnit#5812 ## New Contributors * @mvanhorn made their first contribution in thomhurst/TUnit#5797 **Full Changelog**: thomhurst/TUnit@v1.43.2...v1.43.11 ## 1.43.2 <!-- Release notes generated using configuration in .github/release.yml at v1.43.2 --> ## What's Changed ### Other Changes * fix(assertions): emit auto-generated header in MethodAssertionGenerator output by @JohnVerheij in thomhurst/TUnit#5796 * fix(engine): enforce runtime exclusion for global [NotInParallel] (#5800) by @thomhurst in thomhurst/TUnit#5805 * feat(mocks): add Arg.AnyArgs() shortcut for setup/verify by @thomhurst in thomhurst/TUnit#5792 * fix(should): add specialized assertion sources by @thomhurst in thomhurst/TUnit#5806 ### Dependencies * chore(deps): update tunit to 1.41.0 by @thomhurst in thomhurst/TUnit#5789 * chore(deps): update microsoft.testing to 2.2.2 by @thomhurst in thomhurst/TUnit#5793 * chore(deps): update mstest to 4.2.2 by @thomhurst in thomhurst/TUnit#5794 * chore(deps): update docusaurus by @thomhurst in thomhurst/TUnit#5798 * chore(deps): update dependency dompurify to v3.4.2 by @thomhurst in thomhurst/TUnit#5799 * chore(deps): update dependency mockolate to v3 by @thomhurst in thomhurst/TUnit#5801 **Full Changelog**: thomhurst/TUnit@v1.41.0...v1.43.2 ## 1.41.0 <!-- Release notes generated using configuration in .github/release.yml at v1.41.0 --> ## TUnit Should() Assertions (beta) This versions comes with a beta version of TUnit.Assertions.Should - meaning that you can do: `myCode.Should().BeEqualTo(fluentShouldSyntax);` For more information see the docs: https://tunit.dev/docs/assertions/should-syntax ## What's Changed ### Other Changes * fix(reporters): unwrap TestFailedException for failure grouping by @thomhurst in thomhurst/TUnit#5776 * Fix item-at Satisfies source typing by @thomhurst in thomhurst/TUnit#5764 * ci(docs): add link-check automation and tighten Docusaurus strictness by @thomhurst in thomhurst/TUnit#5779 * feat: add TUnit.Assertions.Should package by @thomhurst in thomhurst/TUnit#5785 ### Dependencies * chore(deps): update tunit to 1.40.10 by @thomhurst in thomhurst/TUnit#5775 * chore(deps): update actions/cache action to v5 by @thomhurst in thomhurst/TUnit#5780 * chore(deps): update dependency microsoft.net.test.sdk to 18.5.1 by @thomhurst in thomhurst/TUnit#5784 **Full Changelog**: thomhurst/TUnit@v1.40.10...v1.41.0 ## 1.40.10 <!-- Release notes generated using configuration in .github/release.yml at v1.40.10 --> ## What's Changed ### Other Changes * refactor(opentelemetry): depend on TUnit.Core instead of umbrella TUnit by @thomhurst in thomhurst/TUnit#5774 ### Dependencies * chore(deps): update tunit to 1.40.5 by @thomhurst in thomhurst/TUnit#5769 **Full Changelog**: thomhurst/TUnit@v1.40.5...v1.40.10 Commits viewable in [compare view](thomhurst/TUnit@v1.40.5...v1.45.0). </details> Updated [TUnit.AspNetCore](https://github.com/thomhurst/TUnit) from 1.40.5 to 1.45.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.AspNetCore's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.45.0 <!-- Release notes generated using configuration in .github/release.yml at v1.45.0 --> ## What's Changed ### Other Changes * fix(generator): place CallerArgumentExpression before params in [GenerateAssertion] emit by @JohnVerheij in thomhurst/TUnit#5940 * fix(sourcegen): drop covariant TActual when [GenerateAssertion] method has its own type parameters by @JohnVerheij in thomhurst/TUnit#5935 * feat(assertions): add CancellationToken overload to WaitsFor and Eventually by @JohnVerheij in thomhurst/TUnit#5938 * fix(html-report): extract categories using MTP Key=name convention by @thomhurst in thomhurst/TUnit#5946 * feat(html-report): rewrite as split-pane design template by @thomhurst in thomhurst/TUnit#5947 ### Dependencies * chore(deps): update microsoft.testing to 2.2.3 by @thomhurst in thomhurst/TUnit#5927 * chore(deps): update mstest to 4.2.3 by @thomhurst in thomhurst/TUnit#5928 * chore(deps): update tunit to 1.44.39 by @thomhurst in thomhurst/TUnit#5929 * chore(deps): update aspire to 13.3.3 by @thomhurst in thomhurst/TUnit#5933 * chore(deps): update dependency dompurify to v3.4.4 by @thomhurst in thomhurst/TUnit#5944 * chore(deps): update dependency qs to v6.15.2 by @thomhurst in thomhurst/TUnit#5941 **Full Changelog**: thomhurst/TUnit@v1.44.39...v1.45.0 ## 1.44.39 <!-- Release notes generated using configuration in .github/release.yml at v1.44.39 --> ## What's Changed ### Other Changes * fix(tests): retry trx read to dodge MTP post-exit flush race on Windows by @thomhurst in thomhurst/TUnit#5888 * fix(pipeline): timeout + retry InstallPlaywrightModule so a hung download fails fast by @thomhurst in thomhurst/TUnit#5889 * fix(otel): require two consecutive idle windows in DrainAsync to catch in-transit POSTs by @thomhurst in thomhurst/TUnit#5890 * test(assertions): drop flaky wall-clock upper bound on WaitsFor timeout test by @thomhurst in thomhurst/TUnit#5886 * fix(sourcegen): drop spurious ')' in MethodAssertionGenerator Task<bool> emit by @JohnVerheij in thomhurst/TUnit#5920 * fix(sourcegen): merge generic parameter lists in [AssertionExtension] emit by @JohnVerheij in thomhurst/TUnit#5921 * fix(aspnetcore): scope correlation processor per-factory to stop cross-factory tag leak by @thomhurst in thomhurst/TUnit#5891 * Changed FSharp.Core version to 10.1.300 by @licon4812 in thomhurst/TUnit#5909 * feat(mocks): add Mock.HttpClientFactory() helper by @thomhurst in thomhurst/TUnit#5894 * Harden WaitsFor timeout test by @thomhurst in thomhurst/TUnit#5926 * fix(sourcegen): emit `default` literal for value-type assertion parameters by @JohnVerheij in thomhurst/TUnit#5919 ### Dependencies * chore(deps): update dependency nunit to 4.6.0 by @thomhurst in thomhurst/TUnit#5826 * chore(deps): update tunit to 1.44.0 by @thomhurst in thomhurst/TUnit#5882 * chore(deps): update dependency mockolate to 3.2.0 by @thomhurst in thomhurst/TUnit#5892 * chore(deps): update dependency yaml to v2.9.0 by @thomhurst in thomhurst/TUnit#5887 * chore(deps): update dependency nuget.protocol to 7.6.0 by @thomhurst in thomhurst/TUnit#5897 * chore(deps): update dependency microsoft.entityframeworkcore to 10.0.8 by @thomhurst in thomhurst/TUnit#5898 * chore(deps): update dependency microsoft.templateengine.authoring.cli to v10.0.300 by @thomhurst in thomhurst/TUnit#5899 * chore(deps): update microsoft.extensions by @thomhurst in thomhurst/TUnit#5905 * chore(deps): update microsoft.aspnetcore to 10.0.8 by @thomhurst in thomhurst/TUnit#5904 * chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to 10.0.300 by @thomhurst in thomhurst/TUnit#5902 * chore(deps): update aspire to 13.3.1 by @thomhurst in thomhurst/TUnit#5900 * chore(deps): update dependency system.commandline to 2.0.8 by @thomhurst in thomhurst/TUnit#5903 * chore(deps): update dependency azure.storage.blobs to 12.28.0 by @thomhurst in thomhurst/TUnit#5910 * chore(deps): update dependency dotnet-sdk to v10.0.300 by @thomhurst in thomhurst/TUnit#5901 * chore(deps): update dependency stackexchange.redis to 2.13.1 by @thomhurst in thomhurst/TUnit#5906 * chore(deps): update aspire to 13.3.2 by @thomhurst in thomhurst/TUnit#5924 * chore(deps): bump mermaid from 11.12.2 to 11.15.0 in /docs by @dependabot[bot] in thomhurst/TUnit#5893 * chore(deps): update dependency streamjsonrpc to 2.24.92 by @thomhurst in thomhurst/TUnit#5915 * chore(deps): update dependency dompurify to v3.4.3 by @thomhurst in thomhurst/TUnit#5913 * chore(deps): update microsoft.build to 18.6.3 by @thomhurst in thomhurst/TUnit#5914 **Full Changelog**: thomhurst/TUnit@v1.44.0...v1.44.39 ## 1.44.0 <!-- Release notes generated using configuration in .github/release.yml at v1.44.0 --> ## What's Changed ### Other Changes * Generated mocks live in the same namespace as the mocked type by @thomhurst in thomhurst/TUnit#5870 * Show multi-step test spans in class timeline, align report ordering with execution, and correlate linked OTel activities by @Copilot in thomhurst/TUnit#5847 * fix: don't leak RUC onto Should-style comparer overloads (#5857) by @thomhurst in thomhurst/TUnit#5873 * Fix culture-dependent timestamp in HTML test report (#5868) by @thomhurst in thomhurst/TUnit#5872 * fix(mocks-http): auto-prepend `/` to partial endpoint paths (#5838) by @thomhurst in thomhurst/TUnit#5874 * Replace Report.ExpandClassTimeline with [ClassTimeline] attribute by @thomhurst in thomhurst/TUnit#5875 * feat(assertions): make ShouldAssertion<T> implement IAssertion (#5824) by @thomhurst in thomhurst/TUnit#5876 * feat(mocks): support non-span ref struct out/ref params by @thomhurst in thomhurst/TUnit#5878 * fix(core): fill optional params when invoking MethodDataSource via reflection by @thomhurst in thomhurst/TUnit#5880 * Mocks: structural fix for Mock<T> / mocked-member name collisions by @thomhurst in thomhurst/TUnit#5881 * chore(mocks): promote TUnit.Mocks packages to stable by @thomhurst in thomhurst/TUnit#5877 ### Dependencies * chore(deps): update tunit to 1.43.41 by @thomhurst in thomhurst/TUnit#5863 * chore(deps): update dependency tunit.assertions.fsharp to 1.43.41 by @thomhurst in thomhurst/TUnit#5865 * chore(deps): bump @babel/plugin-transform-modules-systemjs from 7.28.5 to 7.29.4 in /docs by @dependabot[bot] in thomhurst/TUnit#5867 * chore(deps): bump fast-uri from 3.1.0 to 3.1.2 in /docs by @dependabot[bot] in thomhurst/TUnit#5862 **Full Changelog**: thomhurst/TUnit@v1.43.41...v1.44.0 ## 1.43.41 <!-- Release notes generated using configuration in .github/release.yml at v1.43.41 --> ## What's Changed ### Other Changes * feat(playwright): expose default Context/Launch options on settings by @thomhurst in thomhurst/TUnit#5861 * fix(hooks): resolve TestDiscovery hook context type by attribute kind, not method name by @thomhurst in thomhurst/TUnit#5860 ### Dependencies * chore(deps): update tunit to 1.43.38 by @thomhurst in thomhurst/TUnit#5858 **Full Changelog**: thomhurst/TUnit@v1.43.38...v1.43.41 ## 1.43.38 <!-- Release notes generated using configuration in .github/release.yml at v1.43.38 --> ## What's Changed ### Other Changes * feat(playwright): add TUnitPlaywrightSettings defaults by @thomhurst in thomhurst/TUnit#5859 **Full Changelog**: thomhurst/TUnit@v1.43.37...v1.43.38 ## 1.43.37 <!-- Release notes generated using configuration in .github/release.yml at v1.43.37 --> ## What's Changed ### Other Changes * docs: clarify MethodDataSourceAttribute.Factory is source-generator-managed by @Copilot in thomhurst/TUnit#5835 * fix(assertions): skip ref-struct members in IsEquivalentTo (#5841) by @thomhurst in thomhurst/TUnit#5842 * feat(playwright): add composition-based fixtures by @thomhurst in thomhurst/TUnit#5840 ### Dependencies * chore(deps): update tunit to 1.43.11 by @thomhurst in thomhurst/TUnit#5821 * chore(deps): update dependency polyfill to 10.4.0 by @thomhurst in thomhurst/TUnit#5830 * chore(deps): update dependency polyfill to 10.4.0 by @thomhurst in thomhurst/TUnit#5829 * chore(deps): update react to ^19.2.6 by @thomhurst in thomhurst/TUnit#5839 * chore(deps): update dependency polyfill to 10.5.0 by @thomhurst in thomhurst/TUnit#5848 * chore(deps): update dependency polyfill to 10.5.0 by @thomhurst in thomhurst/TUnit#5849 * chore(deps): update aspire to 13.3.0 by @thomhurst in thomhurst/TUnit#5851 * chore(deps): update dependency brace-expansion to v5.0.6 by @thomhurst in thomhurst/TUnit#5853 * chore(deps): update dependency polyfill to 10.5.1 by @thomhurst in thomhurst/TUnit#5854 * chore(deps): update dependency polyfill to 10.5.1 by @thomhurst in thomhurst/TUnit#5855 * chore(deps): update verify to 31.16.3 by @thomhurst in thomhurst/TUnit#5856 **Full Changelog**: thomhurst/TUnit@v1.43.11...v1.43.37 ## 1.43.11 <!-- Release notes generated using configuration in .github/release.yml at v1.43.11 --> ## What's Changed ### Other Changes * perf(engine): skip execution ledger for independent tests by @thomhurst in thomhurst/TUnit#5813 * perf(engine): skip tracked object graph rediscovery by @thomhurst in thomhurst/TUnit#5814 * fix: suppress XML doc warnings in generated sources by @mvanhorn in thomhurst/TUnit#5797 * Fix xUnit Throws migration docs by @thomhurst in thomhurst/TUnit#5819 * Fix Should optional overload generation by @thomhurst in thomhurst/TUnit#5820 ### Dependencies * chore(deps): update dependency mockolate to 3.1.0 by @thomhurst in thomhurst/TUnit#5811 * chore(deps): update tunit to 1.43.2 by @thomhurst in thomhurst/TUnit#5809 * chore(deps): update dependency nunit.analyzers to 4.13.0 by @thomhurst in thomhurst/TUnit#5815 * chore(deps): update dependency yaml to v2.8.4 by @thomhurst in thomhurst/TUnit#5812 ## New Contributors * @mvanhorn made their first contribution in thomhurst/TUnit#5797 **Full Changelog**: thomhurst/TUnit@v1.43.2...v1.43.11 ## 1.43.2 <!-- Release notes generated using configuration in .github/release.yml at v1.43.2 --> ## What's Changed ### Other Changes * fix(assertions): emit auto-generated header in MethodAssertionGenerator output by @JohnVerheij in thomhurst/TUnit#5796 * fix(engine): enforce runtime exclusion for global [NotInParallel] (#5800) by @thomhurst in thomhurst/TUnit#5805 * feat(mocks): add Arg.AnyArgs() shortcut for setup/verify by @thomhurst in thomhurst/TUnit#5792 * fix(should): add specialized assertion sources by @thomhurst in thomhurst/TUnit#5806 ### Dependencies * chore(deps): update tunit to 1.41.0 by @thomhurst in thomhurst/TUnit#5789 * chore(deps): update microsoft.testing to 2.2.2 by @thomhurst in thomhurst/TUnit#5793 * chore(deps): update mstest to 4.2.2 by @thomhurst in thomhurst/TUnit#5794 * chore(deps): update docusaurus by @thomhurst in thomhurst/TUnit#5798 * chore(deps): update dependency dompurify to v3.4.2 by @thomhurst in thomhurst/TUnit#5799 * chore(deps): update dependency mockolate to v3 by @thomhurst in thomhurst/TUnit#5801 **Full Changelog**: thomhurst/TUnit@v1.41.0...v1.43.2 ## 1.41.0 <!-- Release notes generated using configuration in .github/release.yml at v1.41.0 --> ## TUnit Should() Assertions (beta) This versions comes with a beta version of TUnit.Assertions.Should - meaning that you can do: `myCode.Should().BeEqualTo(fluentShouldSyntax);` For more information see the docs: https://tunit.dev/docs/assertions/should-syntax ## What's Changed ### Other Changes * fix(reporters): unwrap TestFailedException for failure grouping by @thomhurst in thomhurst/TUnit#5776 * Fix item-at Satisfies source typing by @thomhurst in thomhurst/TUnit#5764 * ci(docs): add link-check automation and tighten Docusaurus strictness by @thomhurst in thomhurst/TUnit#5779 * feat: add TUnit.Assertions.Should package by @thomhurst in thomhurst/TUnit#5785 ### Dependencies * chore(deps): update tunit to 1.40.10 by @thomhurst in thomhurst/TUnit#5775 * chore(deps): update actions/cache action to v5 by @thomhurst in thomhurst/TUnit#5780 * chore(deps): update dependency microsoft.net.test.sdk to 18.5.1 by @thomhurst in thomhurst/TUnit#5784 **Full Changelog**: thomhurst/TUnit@v1.40.10...v1.41.0 ## 1.40.10 <!-- Release notes generated using configuration in .github/release.yml at v1.40.10 --> ## What's Changed ### Other Changes * refactor(opentelemetry): depend on TUnit.Core instead of umbrella TUnit by @thomhurst in thomhurst/TUnit#5774 ### Dependencies * chore(deps): update tunit to 1.40.5 by @thomhurst in thomhurst/TUnit#5769 **Full Changelog**: thomhurst/TUnit@v1.40.5...v1.40.10 Commits viewable in [compare view](thomhurst/TUnit@v1.40.5...v1.45.0). </details> 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>
Summary
[InheritsTests]derived class on a generic abstract base + a[MethodDataSource]method with an optional parameter (e.g.[EnumeratorCancellation] CancellationToken ct = default) threwTargetParameterCountExceptionat discovery.MethodDataSourceAttribute.GetDataRowsAsynccalledmethodInfo.Invoke(instance, Arguments)withArguments == []; reflection'sInvokedoes not auto-fill defaults like a C# call site. The source-genFactorypath emits a direct C# call (compiler fills defaults), but the reflection fallback used for the generic abstract base case did not — so the optional CT parameter triggered the count mismatch.BuildInvokeArgs(MethodInfo, object?[])that pads the supplied args to the method's parameter count, filling missing slots withParameterInfo.DefaultValue, thenCancellationToken.NoneforCancellationToken-typed params without an explicit default, then falling back to the original array so genuine required-param mismatches still surface the originalTargetParameterCountException.Behaviour parity with source-gen
The source generator emits
DataSource()(TestMetadataGenerator.cs:1628) — the C# compiler then fillsdefault(CancellationToken). The reflection path now matches that exactly (CancellationToken.None). Plumbing a build-time CT throughIDataSourceAttribute.GetDataRowsAsyncwould be a public-API change and is out of scope for this bug fix.Test plan
TUnit.TestProject/Bugs/5879/Repro.cs— exact repro from the issue, marked[EngineTest(ExpectedResult.Pass)]. ThrowsTargetParameterCountExceptionbefore the fix; passes after.dotnet test TUnit.TestProject --treenode-filter "/*/*/Issue5879Tests/*"→ 1/1 pass.dotnet test TUnit.UnitTests→ 191/191 pass (no regressions in adjacent paths).