Skip to content

test(azure-functions): replace NSubstitute with TUnit.Mocks in integration tests - #740

Merged
samtrion merged 1 commit into
mainfrom
test/remove-nsubstitute-tunit-mocks
Jul 30, 2026
Merged

test(azure-functions): replace NSubstitute with TUnit.Mocks in integration tests#740
samtrion merged 1 commit into
mainfrom
test/remove-nsubstitute-tunit-mocks

Conversation

@samtrion

Copy link
Copy Markdown
Contributor

Summary

  • Removes the last NSubstitute usage in the repository — the IInvocationFeatures mock in NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration — and drops the package entirely.
  • Bumps TUnit and TUnit.Mocks to 1.63.0. This bump is part of the fix, not an incidental dependency update.

Why it works now

Two separate blockers, both resolved:

  1. Compile time (CS1061). SetupHttpRequestFeature took the feature collection as IInvocationFeatures. On the interface type, Get<T>() is the real method returning T, so there is nothing to chain .Returns() onto. Typing the parameter as Mock<IInvocationFeatures> — matching the existing Mock<FunctionContext> parameter — routes the call through the generated mock. Suggested by @thomhurst in [TUnit.Mocks] Get<T>() can't be configured for T inaccessible to the calling assembly (unlike NSubstitute's runtime-proxy auto-recursion) thomhurst/TUnit#6514.
  2. Runtime (version floor). The middleware always calls context.GetInvocationResult(), which internally requests the internal Worker type IFunctionBindingsFeature via Get<T>() — a type this assembly cannot name. Verified locally: on 1.62.0 that call returns null and GetRequired<T>() throws InvalidOperationException: No feature is registered with the type Microsoft.Azure.Functions.Worker.Context.Features.IFunctionBindingsFeature. On 1.63.0 the generated mock auto-stubs it and the tests pass. Both conditions are required; either one alone fails.

Both facts are recorded as comments in TestBase.cs so a future version pin or downgrade does not silently resurrect the failure.

This supersedes the closed #738, which worked around the same problem with a hand-written TestInvocationFeatures plus a DispatchProxy fallback. Those two helper types are no longer needed.

Test plan

  • dotnet test — 167/167 passing across net8.0/net9.0/net10.0
  • Verified the failure mode on 1.62.0 to confirm the version floor is real

Refs #723, thomhurst/TUnit#6514

…ation tests

The feature collection was the last NSubstitute usage in the repository. It
could not be migrated because `IInvocationFeatures.Get<T>()` was called on the
interface type, where it resolves to the real method returning `T` and offers
nothing to chain `.Returns()` onto (CS1061). Typing the parameter as
`Mock<IInvocationFeatures>` routes the call through the generated mock instead.

Bumps TUnit and TUnit.Mocks to 1.63.0, which is required rather than
incidental: the middleware always calls `context.GetInvocationResult()`, which
internally requests the internal Worker type `IFunctionBindingsFeature` via
`Get<T>()`. Only from 1.63.0 on does the generated mock auto-stub a type
argument this assembly cannot name; on 1.62.0 the call returns null and
`GetRequired<T>()` throws "No feature is registered with the type ...".

Removes the NSubstitute package reference and version entry.

Refs #723, thomhurst/TUnit#6514
@samtrion
samtrion requested a review from a team as a code owner July 30, 2026 20:11
@samtrion
samtrion requested review from benwirren and removed request for a team July 30, 2026 20:11
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • state:ready for merge

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ac492e9-6538-48f4-adb1-3e1938e1c82b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.71%. Comparing base (1e56c99) to head (6ebc53f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #740   +/-   ##
=======================================
  Coverage   97.71%   97.71%           
=======================================
  Files          20       20           
  Lines         175      175           
  Branches       22       22           
=======================================
  Hits          171      171           
  Misses          2        2           
  Partials        2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samtrion
samtrion merged commit 85911ea into main Jul 30, 2026
14 checks passed
@samtrion
samtrion deleted the test/remove-nsubstitute-tunit-mocks branch July 30, 2026 20:28
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.

1 participant