fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161)#6183
Conversation
A [MethodDataSource] returning IEnumerable<Func<T>> invokes the Func and spreads a tuple result across the test method parameters. The same source wrapped as IEnumerable<TestDataRow<Func<T>>> did not: the inner Func was passed through as a single argument, so it could not bind to the test parameters and parameterized DisplayName placeholders never resolved (causing rows with the same template to collapse into duplicates). - TestDataRowUnwrapper.UnwrapArray now invokes the inner Func after unwrapping the TestDataRow, so TestDataRow<Func<T>> behaves identically to a bare Func<T> source. This is the single convergence point shared by both source-generated and reflection modes, so no codegen changes are needed. - TestDataAnalyzer strips Func<> and TestDataRow<> layers in a loop, so TestDataRow<Func<T>> (as well as Func<TestDataRow<T>>) reduces to the inner type and no longer reports a false parameter-count diagnostic. - DisplayNameSubstitutor now performs word-boundary-aware placeholder replacement and resolves positional $argN before $paramName. This fixes a pre-existing prefix-collision bug where a parameter named "a" was matched inside "$arg1" (rendering "1rg1"), also covered by the existing CustomDisplayNameTests.TestParameterNamePrefixBug. Adds Bugs/6161 runtime tests (source-gen + reflection) and an analyzer no-diagnostic test, and documents the tuple + parameterized DisplayName case.
Up to standards ✅🟢 Issues
|
There was a problem hiding this comment.
Code Review
This is a well-targeted fix for issue #6161. The three changes are cohesive and the root-cause reasoning is sound.
TestDataRowUnwrapper.cs — Core fix
data = DataSourceHelpers.InvokeIfFunc(data);Placing this right after TryUnwrap is exactly the right spot. The comment correctly identifies why InvokeIfFunc doesn't help for TestDataRow<Func<T>> in DataSourceHelpers.ToObjectArray — the Func is opaque there, only surfacing after the TryUnwrap boundary. Simple and correct.
TestDataAnalyzer.cs — do/while loop
The loop approach is elegant. Because both if blocks can fire in the same iteration, Func<TestDataRow<T>> collapses to T in a single pass rather than requiring a second loop. Safe against infinite iteration since C# generics cannot form cycles — each iteration necessarily moves inward.
One observation: isFunc is set to true on any Func<> peel, including a second peel in a subsequent iteration (e.g., Func<Func<T>>). This is consistent with the existing semantics of isFunc — it signals that tuple-spread applies — which is correct for any Func in the chain. Not a bug, just worth being aware of.
DisplayNameSubstitutor.cs — ReplacePlaceholder + ordering fix
Ordering change (positional before named): Correct. Without this, $a (named param) matches the $a prefix inside $arg1, corrupting the output before the positional pass ever runs.
ReplacePlaceholder boundary check: The word-boundary logic is correct. I traced through the $arg1 / $arg10 interaction manually — $arg1 inside $arg10 is correctly skipped (the 0 after $arg1 is an identifier char), while standalone $arg1 is replaced.
One minor edge case to be aware of: since IsIdentifierChar includes _, a display name template like "$a_description" where a is a parameter name will not substitute $a there (because _ counts as an identifier char following $a). This is actually the correct/expected behavior in C# identifier semantics — a_description would be a different name — but users who write $a_suffix expecting $a to be replaced will be surprised. Not a regression from this PR, and the behavior is defensible; just worth documenting if it surfaces as a user complaint.
Potential substitution-order collision: If a user names a parameter arg1, the positional pass resolves $arg1 first (as the first argument's value), and the named pass for parameters[0] (named arg1) then searches for $arg1 in the already-substituted string. In practice this won't matter because the positional value is unlikely to re-introduce $arg1, but it's a latent edge case in any linear-substitution scheme.
Test coverage
The [After(Class)] deduplication guard in Tests.cs is a particularly good regression anchor — it directly tests the symptom that motivated the bug report (multiple rows collapsing to a single execution). The combination of tuple and reference-type test cases (TupleFuncIsUnwrapped / ReferenceFuncIsUnwrapped) is thorough.
Documentation
The doc addition accurately describes the behavior and the code example matches the regression test, which is a nice consistency detail.
Overall: The fix is correct, well-tested, and the implementation choices are sound. No blocking concerns — the edge cases noted above are pre-existing design tradeoffs rather than regressions. Good work.
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.48.6 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 Commits viewable in [compare view](thomhurst/TUnit@v1.48.6...v1.51.0). </details> [](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>
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.45.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 ## 1.48.6 <!-- Release notes generated using configuration in .github/release.yml at v1.48.6 --> ## What's Changed ### Other Changes * fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) by @thomhurst in thomhurst/TUnit#6141 ### Dependencies * chore(deps): update tunit to 1.48.0 by @thomhurst in thomhurst/TUnit#6135 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6137 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6138 * chore(deps): update verify to 31.19.0 by @thomhurst in thomhurst/TUnit#6139 **Full Changelog**: thomhurst/TUnit@v1.48.0...v1.48.6 ## 1.48.0 <!-- Release notes generated using configuration in .github/release.yml at v1.48.0 --> ## What's Changed ### Other Changes * feat(html-report): baked-in C# syntax highlighting on Source tab by @slang25 in thomhurst/TUnit#6132 * feat(analyzers): suppress VSTHRD200 on test and hook methods by @thomhurst in thomhurst/TUnit#6123 * fix(source-gen): correct source location for cross-project inherited tests by @slang25 in thomhurst/TUnit#6133 * feat(assertions): add WasCalled to tunit mocks assertions by @robertcoltheart in thomhurst/TUnit#6126 * feat(arguments): bind array values to a single array test parameter by @thomhurst in thomhurst/TUnit#6122 * fix: populate retry/flaky attempt history in HTML report (#6119) by @thomhurst in thomhurst/TUnit#6124 ### Dependencies * chore(deps): update tunit to 1.47.0 by @thomhurst in thomhurst/TUnit#6115 * chore(deps): update dependency microsoft.visualstudio.threading.analyzers to 17.14.15 by @thomhurst in thomhurst/TUnit#6134 **Full Changelog**: thomhurst/TUnit@v1.47.0...v1.48.0 ## 1.47.0 <!-- Release notes generated using configuration in .github/release.yml at v1.47.0 --> ## What's Changed ### Other Changes * perf(engine): hoist GetParameters and dict-dedup AfterTestDiscovery hooks by @thomhurst in thomhurst/TUnit#6062 * perf(engine): hoist GetParameters and drop LINQ in reflection discovery by @thomhurst in thomhurst/TUnit#6063 * perf(engine): cache treenode filter path on TestMetadata by @thomhurst in thomhurst/TUnit#6064 * perf: use is T pattern in ReflectionExtensions.HasAttribute fallback (#6060) by @thomhurst in thomhurst/TUnit#6066 * perf: replace OrderBy().ToArray() with Array.Sort in ConstraintKeyScheduler by @thomhurst in thomhurst/TUnit#6067 * perf: pool HashSet in WaitingTestIndex.GetCandidatesForReleasedKeys by @thomhurst in thomhurst/TUnit#6069 * perf: collapse OfType chains in JUnitXmlWriter (#6052) by @thomhurst in thomhurst/TUnit#6070 * perf(engine): avoid closure allocation in AfterHookPairTracker.GetOrCreateAfterAssemblyTask (#6041) by @thomhurst in thomhurst/TUnit#6071 * perf: avoid closure allocation in BeforeHookTaskCache.GetOrCreateBeforeAssemblyTask (#6040) by @thomhurst in thomhurst/TUnit#6073 * perf: use TryAdd in TestDependencyResolver dependency dedupe by @thomhurst in thomhurst/TUnit#6068 * perf: replace LINQ Any with foreach in TestGenericTypeResolver (#6044) by @thomhurst in thomhurst/TUnit#6072 * perf: avoid Cast<object>().FirstOrDefault() iterator alloc in CastHelper (#6029) by @thomhurst in thomhurst/TUnit#6074 * perf(engine): avoid string round-trip when building nested type names (#6049) by @thomhurst in thomhurst/TUnit#6075 * perf(engine): replace Select+ToArray with manual Type[] build (#6043) by @thomhurst in thomhurst/TUnit#6076 * perf(core): replace OfType().FirstOrDefault()/.Any() with foreach in ClassConstructorHelper by @thomhurst in thomhurst/TUnit#6078 * perf(engine): avoid FirstOrDefault iterator alloc in TestGenericTypeResolver by @thomhurst in thomhurst/TUnit#6079 * perf(engine): use SearchValues<char> for reporter filename sanitization by @thomhurst in thomhurst/TUnit#6090 * perf: dedupe TestDataFormatter.FormatArguments with pooled StringBuilder by @thomhurst in thomhurst/TUnit#6088 * perf(engine): use MemoryExtensions.Split for path parsing in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6085 * perf(engine): use CollectionsMarshal.GetValueRefOrAddDefault for dictionary index builds by @thomhurst in thomhurst/TUnit#6086 * perf(engine): replace LINQ Where closure with inline filter in MetadataDependencyExpander BFS by @thomhurst in thomhurst/TUnit#6084 * perf(engine): pool StringBuilder in DisplayNameBuilder.FormatArguments by @thomhurst in thomhurst/TUnit#6082 * Preserve specialized chaining after null assertions by @thomhurst in thomhurst/TUnit#6008 * perf: use EnumerateLines for line splitting in HtmlReportGenerator by @thomhurst in thomhurst/TUnit#6089 * perf: collapse Replace chain in TestNameFormatter.BuildTestId by @thomhurst in thomhurst/TUnit#6083 * perf: use OrdinalIgnoreCase Contains in HtmlReportGenerator span mapping by @thomhurst in thomhurst/TUnit#6093 * perf(assertions): avoid eager interpolated-string alloc in assertion source ctors by @thomhurst in thomhurst/TUnit#6091 * perf: optimize TestNameFormatter argument and bool formatting by @thomhurst in thomhurst/TUnit#6095 * perf: use FrozenSet/FrozenDictionary for read-only static lookups by @thomhurst in thomhurst/TUnit#6099 * perf: avoid GetCustomAttributes() + LINQ chain for per-property attribute scans by @thomhurst in thomhurst/TUnit#6098 * perf(engine): replace magic-string RequiredAttribute match with type check in ConstructorHelper by @thomhurst in thomhurst/TUnit#6087 * perf(core): replace Select+Func factory chain in DataSourceHelpers by @thomhurst in thomhurst/TUnit#6081 * perf: replace LINQ dependency extraction with manual loop by @thomhurst in thomhurst/TUnit#6096 * perf(core): avoid string[] alloc in ArgumentFormatter.FormatArguments by @thomhurst in thomhurst/TUnit#6080 * perf: use [GeneratedRegex] in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6094 * perf: dedupe GetSimpleTypeName into shared TypeNameFormatter by @thomhurst in thomhurst/TUnit#6097 * fix: remove GitVersion MSBuild task, pin local builds to 99.99.99 (#6077) by @thomhurst in thomhurst/TUnit#6101 * HTML Report: source link + code snippet on Source tab (#5993) by @thomhurst in thomhurst/TUnit#6100 * perf(sourcegen): Single-pass attribute classification by @thomhurst in thomhurst/TUnit#6111 * perf(core): eliminate per-test allocations in TestDetails/HookMethod by @thomhurst in thomhurst/TUnit#6109 * perf: hoist char[] alloc in FsCheckPropertyTestExecutor to static SearchValues by @thomhurst in thomhurst/TUnit#6108 * perf(core): de-LINQ data-source expansion by @thomhurst in thomhurst/TUnit#6110 * perf: avoid LINQ chains in TestDependency equality and MethodDataSourceAttribute method matching by @thomhurst in thomhurst/TUnit#6092 * perf(engine): reduce allocations in reflection-mode discovery/execution by @thomhurst in thomhurst/TUnit#6113 * perf(assertions): allocation-free passing path (TUnit.Assertions) by @thomhurst in thomhurst/TUnit#6112 ### Dependencies ... (truncated) ## 1.46.0 <!-- Release notes generated using configuration in .github/release.yml at v1.46.0 --> ## What's Changed ### Other Changes * docs: add Rider VSTest conflict troubleshooting by @smolchanovsky in thomhurst/TUnit#5989 * Populate generated test metadata with full source spans by @Copilot in thomhurst/TUnit#5991 * Add devcontainer configuration by @Copilot in thomhurst/TUnit#5995 * fix: treenode filter pre-filter rejects parenthesised segments (#6026) by @thomhurst in thomhurst/TUnit#6027 * fix(engine): isolate per-session state under MTP server-mode concurrency (#6001) by @thomhurst in thomhurst/TUnit#6025 ### Dependencies * chore(deps): update dependency stackexchange.redis to 2.13.10 by @thomhurst in thomhurst/TUnit#5985 * chore(deps): update tunit to 1.45.29 by @thomhurst in thomhurst/TUnit#5986 * chore(deps): update dependency mockolate to 3.2.1 by @thomhurst in thomhurst/TUnit#5987 * chore(deps): update dependency microsoft.playwright to 1.60.0 by @thomhurst in thomhurst/TUnit#5988 * chore(deps): update dependency messagepack to 3.1.6 by @thomhurst in thomhurst/TUnit#5992 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5998 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5997 * chore(deps): update verify to 31.17.0 by @thomhurst in thomhurst/TUnit#6000 * chore(deps): update verify to 31.18.0 by @thomhurst in thomhurst/TUnit#6013 * chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by @thomhurst in thomhurst/TUnit#6016 * chore(deps): update dependency dompurify to v3.4.6 by @thomhurst in thomhurst/TUnit#6015 * chore(deps): update dependency dompurify to v3.4.7 by @thomhurst in thomhurst/TUnit#6019 * chore(deps): update dependency npgsql to 10.0.3 by @thomhurst in thomhurst/TUnit#6020 * chore(deps): update dependency stackexchange.redis to 2.13.17 by @thomhurst in thomhurst/TUnit#6021 * chore(deps): update dependency npgsql.entityframeworkcore.postgresql to 10.0.2 by @thomhurst in thomhurst/TUnit#6022 ## New Contributors * @smolchanovsky made their first contribution in thomhurst/TUnit#5989 **Full Changelog**: thomhurst/TUnit@v1.45.29...v1.46.0 ## 1.45.29 <!-- Release notes generated using configuration in .github/release.yml at v1.45.29 --> ## What's Changed ### Other Changes * Fix shared fixture lifetime for reused discovery instances by @thomhurst in thomhurst/TUnit#5983 * Preserve override accessibility in generated mocks by @thomhurst in thomhurst/TUnit#5984 ### Dependencies * chore(deps): update tunit to 1.45.22 by @thomhurst in thomhurst/TUnit#5974 * chore(deps): update dependency messagepack to 3.1.5 by @thomhurst in thomhurst/TUnit#5978 * chore(deps): update aspire to 13.3.5 by @thomhurst in thomhurst/TUnit#5980 **Full Changelog**: thomhurst/TUnit@v1.45.22...v1.45.29 ## 1.45.22 <!-- Release notes generated using configuration in .github/release.yml at v1.45.22 --> ## What's Changed ### Other Changes * Remove ".NET" from Aspire references by @antmdvs in thomhurst/TUnit#5968 * Fix chained mock setup behavior by @thomhurst in thomhurst/TUnit#5973 ### Dependencies * chore(deps): update tunit to 1.45.8 by @thomhurst in thomhurst/TUnit#5958 * chore(deps): update dependency nunit to 4.6.1 by @thomhurst in thomhurst/TUnit#5961 * chore(deps): update dependency testcontainers.postgresql to 4.12.0 by @thomhurst in thomhurst/TUnit#5963 * chore(deps): update dependency testcontainers.redis to 4.12.0 by @thomhurst in thomhurst/TUnit#5965 * chore(deps): update dependency testcontainers.kafka to 4.12.0 by @thomhurst in thomhurst/TUnit#5962 * chore(deps): update aspire to 13.3.4 by @thomhurst in thomhurst/TUnit#5966 * chore(deps): bump webpack-dev-server from 5.2.2 to 5.2.4 in /docs by @dependabot[bot] in thomhurst/TUnit#5964 ## New Contributors * @antmdvs made their first contribution in thomhurst/TUnit#5968 **Full Changelog**: thomhurst/TUnit@v1.45.8...v1.45.22 ## 1.45.8 <!-- Release notes generated using configuration in .github/release.yml at v1.45.8 --> ## What's Changed ### Other Changes * fix(aspire): route CreateHttpClient through IHttpClientFactory by @thomhurst in thomhurst/TUnit#5957 ### Dependencies * chore(deps): update tunit to 1.45.0 by @thomhurst in thomhurst/TUnit#5949 * chore(deps): update dependency dompurify to v3.4.5 by @thomhurst in thomhurst/TUnit#5951 * chore(deps): update dependency microsoft.testing.extensions.codecoverage to 18.7.0 by @thomhurst in thomhurst/TUnit#5953 * chore(deps): update dependency coverlet.collector to 10.0.1 by @thomhurst in thomhurst/TUnit#5952 * chore(deps): update dependency polyfill to 10.6.0 by @thomhurst in thomhurst/TUnit#5955 * chore(deps): update dependency polyfill to 10.6.0 by @thomhurst in thomhurst/TUnit#5954 **Full Changelog**: thomhurst/TUnit@v1.45.0...v1.45.8 Commits viewable in [compare view](thomhurst/TUnit@v1.45.0...v1.51.0). </details> [](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>
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.49.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 Commits viewable in [compare view](thomhurst/TUnit@v1.49.0...v1.51.0). </details> Updated [TUnit.AspNetCore](https://github.com/thomhurst/TUnit) from 1.49.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.AspNetCore's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 Commits viewable in [compare view](thomhurst/TUnit@v1.49.0...v1.51.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>
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.48.6 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.Core's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 Commits viewable in [compare view](thomhurst/TUnit@v1.48.6...v1.51.0). </details> [](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>
Problem
Fixes #6161.
A
[MethodDataSource]returningIEnumerable<Func<T>>(orIEnumerable<Func<(int,int,int)>>) already invokes theFuncand spreads the tuple result across the test method parameters. The same source wrapped asIEnumerable<TestDataRow<Func<T>>>did not — the innerFuncwas passed through as a single argument. Consequences:TestMethod(Func<...> f), notTestMethod(int a, int b, int c).DisplayNameplaceholders ($arg1,$First, …) couldn't bind to the real values.The behaviour was already implied by the docs (
TestDataRow<Func<HttpClient>>"to ensure fresh instances"), so this aligns the code with the documented intent.Changes
TestDataRowUnwrapper.UnwrapArray— invokes the innerFuncafter unwrapping theTestDataRow, soTestDataRow<Func<T>>behaves identically to a bareFunc<T>source (invoke, then spread tuples). This is the single convergence point shared by both source-generated and reflection modes, so there is no codegen change and no snapshot churn.TestDataAnalyzer— stripsFunc<>andTestDataRow<>wrapper layers in a loop, soTestDataRow<Func<T>>(andFunc<TestDataRow<T>>) reduce to the innerTbefore the tuple check. Prevents a false parameter-count diagnostic that would otherwise block the new pattern at compile time.DisplayNameSubstitutor— word-boundary-aware placeholder replacement, resolving positional$argNbefore$paramName. Fixes a pre-existing prefix-collision bug where a parameter namedawas matched inside$arg1(rendering1rg1instead of1). This is the bug already documented byCustomDisplayNameTests.TestParameterNamePrefixBug.Tests
TUnit.TestProject/Bugs/6161/Tests.cs—TestDataRow<Func<(int,int,int)>>→(int a, int b, int c)with"$arg1 + $arg2 = $arg3", plusTestDataRow<Func<record>>→ single-param. Passes in source-gen and reflection modes; display names render1 + 1 = 2,1 + 2 = 3,2 + 3 = 5. An[After(Class)]hook asserts all three distinct rows ran (dedup regression).MethodDataSourceAnalyzerTests.Method_Data_Source_With_TestDataRow_Func_Tuple_No_Error— no-diagnostic coverage for the inner-Funcnesting order.Verification
CustomDisplayNameTests: 12/12 (incl. the now-fixed prefix bug).