Skip to content

fix: forward wrapped access of interface members via the declaring interface - #849

Merged
vbreuss merged 2 commits into
mainfrom
fix/sibling-interface-wrapping
Aug 26, 2026
Merged

fix: forward wrapped access of interface members via the declaring interface#849
vbreuss merged 2 commits into
mainfrom
fix/sibling-interface-wrapping

Conversation

@vbreuss

@vbreuss vbreuss commented Aug 26, 2026

Copy link
Copy Markdown
Member

Mocking an interface that inherits two sibling interfaces declaring a member with the same name did not compile: the implicit member forwarded to the wrapped instance through the mocked interface, where the access is ambiguous between the siblings (CS0229 for properties and events, CS0121 for methods and indexers).

Always cast MockRegistry.Wraps to the declaring interface (ContainingType) for interface mocks. This subsumes the special case for explicit implementations (hidden members), whose ExplicitImplementation is the containing type anyway. Class mocks keep forwarding through the mocked class.

…terface

Mocking an interface that inherits two sibling interfaces declaring a member with the same name did not compile: the implicit member forwarded to the wrapped instance through the mocked interface, where the access is ambiguous between the siblings (CS0229 for properties and events, CS0121 for methods and indexers).

Always cast `MockRegistry.Wraps` to the declaring interface (`ContainingType`) for interface mocks. This subsumes the special case for explicit implementations (hidden members), whose `ExplicitImplementation` is the containing type anyway. Class mocks keep forwarding through the mocked class.
@vbreuss vbreuss self-assigned this Aug 26, 2026
Copilot AI lite review requested due to automatic review settings August 26, 2026 18:53
@vbreuss vbreuss added the bug Something isn't working label Aug 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a source-generator wrapping edge case for interface mocks where a mocked interface inherits multiple sibling interfaces that declare same-named members. The generated forwarding code now casts MockRegistry.Wraps to the declaring interface (ContainingType) so member access is unambiguous and compiles correctly (avoiding CS0229/CS0121), while class mocks continue forwarding through the mocked class type.

Changes:

  • Update interface-member forwarding in the source generator to delegate through each member’s declaring interface when wrapping.
  • Add runtime and generator tests covering sibling-interface collisions across properties, indexers, methods, and events.
  • Update docs to reflect the clarified wrapping/forwarding behavior for interface members.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Tests/Mockolate.Tests/TestHelpers/IChocolateGiftSet.cs Adds sibling-interface hierarchy used to reproduce/verify ambiguous-member wrapping scenarios.
Tests/Mockolate.Tests/MockTests.WrappingInterfaceTests.cs Adds runtime wrapping tests proving correct delegation for sibling-interface members.
Tests/Mockolate.SourceGenerators.Tests/MockTests.cs Adds generator regression tests ensuring generated code delegates via declaring interfaces for sibling collisions.
Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs Updates expected generated output for wrapping delegation to use declaring interfaces in base-interface members.
Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs Updates expected generated output for wrapping delegation to use declaring interfaces in base-interface members.
Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.EventsTests.cs Updates expected generated output for wrapping delegation to use declaring interfaces in base-interface members.
Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs Implements the core generator change: cast wraps to ContainingType for interface mocks when forwarding.
Docs/pages/01-create-mocks.md Documents that interface members are forwarded through their declaring interfaces (including new hiding and sibling collisions).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown

Test Results

    24 files  ± 0      24 suites  ±0   12m 40s ⏱️ + 1m 16s
 4 377 tests +11   4 375 ✅ +11  2 💤 ±0  0 ❌ ±0 
27 799 runs  +61  27 795 ✅ +61  4 💤 ±0  0 ❌ ±0 

Results for commit 40a5755. ± Comparison against base commit cb55194.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V45 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Event Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 304.7 ns 2.99 ns 2.80 ns 1.58 1.7 KB 1.00
Mockolate 193.1 ns 3.44 ns 3.22 ns 1.00 1.7 KB 1.00
Imposter 768.4 ns 18.85 ns 17.63 ns 3.98 8.8 KB 5.17
TUnitMocks 112.0 ns 3.58 ns 3.35 ns 0.58 1.34 KB 0.79
Moq 7,515.3 ns 113.15 ns 105.84 ns 38.93 12.51 KB 7.34
NSubstitute 2,881.7 ns 56.91 ns 50.45 ns 14.93 9.05 KB 5.31
FakeItEasy 100,072.4 ns 2,744.54 ns 2,567.25 ns 518.39 15.27 KB 8.97
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 3.23GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 340.2 ns 9.63 ns 9.00 ns 0.99 1.93 KB 1.00
Mockolate 1 342.3 ns 4.73 ns 4.20 ns 1.00 1.93 KB 1.00
Imposter 1 553.0 ns 13.10 ns 12.25 ns 1.62 4.04 KB 2.09
TUnitMocks 1 474.4 ns 8.79 ns 8.22 ns 1.39 2.02 KB 1.04
Moq 1 186,694.1 ns 693.69 ns 614.94 ns 545.44 14.73 KB 7.63
NSubstitute 1 6,240.7 ns 70.51 ns 65.96 ns 18.23 9.06 KB 4.70
FakeItEasy 1 6,164.7 ns 78.44 ns 73.37 ns 18.01 8.05 KB 4.17
baseline* 10 677.6 ns 5.93 ns 5.55 ns 1.04 2.14 KB 1.00
Mockolate 10 651.9 ns 7.02 ns 6.57 ns 1.00 2.14 KB 1.00
Imposter 10 1,150.4 ns 13.87 ns 12.30 ns 1.76 5.52 KB 2.58
TUnitMocks 10 1,371.4 ns 28.90 ns 27.03 ns 2.10 3.73 KB 1.74
Moq 10 190,327.8 ns 504.98 ns 394.25 ns 292.00 18.64 KB 8.71
NSubstitute 10 9,472.1 ns 83.32 ns 73.86 ns 14.53 11.52 KB 5.38
FakeItEasy 10 9,755.5 ns 127.99 ns 119.73 ns 14.97 15.42 KB 7.20
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v4

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Indexer N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 1.067 μs 0.0175 μs 0.0163 μs 0.96 3.77 KB 1.00
Mockolate 1 1.112 μs 0.0171 μs 0.0151 μs 1.00 3.77 KB 1.00
Imposter 1 1.128 μs 0.0085 μs 0.0071 μs 1.01 5.16 KB 1.37
Moq 1 168.328 μs 0.6968 μs 0.6518 μs 151.45 20.47 KB 5.44
NSubstitute 1 11.447 μs 0.0376 μs 0.0333 μs 10.30 12.78 KB 3.39
FakeItEasy 1 11.774 μs 0.0581 μs 0.0544 μs 10.59 13.62 KB 3.62
baseline* 10 2.929 μs 0.0082 μs 0.0077 μs 0.98 4.82 KB 1.00
Mockolate 10 2.988 μs 0.0112 μs 0.0105 μs 1.00 4.82 KB 1.00
Imposter 10 2.406 μs 0.0330 μs 0.0292 μs 0.81 7.97 KB 1.65
Moq 10 179.856 μs 1.0002 μs 0.9356 μs 60.19 30 KB 6.22
NSubstitute 10 24.961 μs 0.0602 μs 0.0563 μs 8.35 25.58 KB 5.31
FakeItEasy 10 24.544 μs 0.0708 μs 0.0662 μs 8.21 32.98 KB 6.84
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.73GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Callback Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 308.8 ns 3.53 ns 2.76 ns 0.82 1.57 KB 1.00
Mockolate 378.3 ns 6.10 ns 5.70 ns 1.00 1.57 KB 1.00
Imposter 501.4 ns 6.81 ns 6.37 ns 1.33 2.38 KB 1.52
TUnitMocks 562.5 ns 4.68 ns 4.38 ns 1.49 1.99 KB 1.27
Moq 101,164.2 ns 807.59 ns 755.42 ns 267.51 8.88 KB 5.66
NSubstitute 5,016.0 ns 15.58 ns 13.81 ns 13.26 7.71 KB 4.91
FakeItEasy 5,126.1 ns 37.15 ns 34.75 ns 13.55 6.81 KB 4.33
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Property N Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 1 527.8 ns 7.16 ns 6.70 ns 0.89 2.41 KB 1.00
Mockolate 1 594.8 ns 28.31 ns 26.48 ns 1.00 2.41 KB 1.00
Imposter 1 456.3 ns 8.16 ns 7.63 ns 0.77 3.13 KB 1.29
TUnitMocks 1 498.2 ns 7.49 ns 7.00 ns 0.84 1.64 KB 0.68
Moq 1 12,370.6 ns 87.79 ns 77.82 ns 20.83 10.27 KB 4.25
NSubstitute 1 7,971.8 ns 81.74 ns 76.46 ns 13.43 11.45 KB 4.74
FakeItEasy 1 8,829.0 ns 54.81 ns 42.79 ns 14.87 11.24 KB 4.66
baseline* 10 1,054.4 ns 7.69 ns 7.20 ns 0.96 2.91 KB 1.00
Mockolate 10 1,095.2 ns 23.25 ns 21.75 ns 1.00 2.91 KB 1.00
Imposter 10 1,156.4 ns 15.72 ns 13.93 ns 1.06 4.67 KB 1.61
TUnitMocks 10 1,672.0 ns 33.96 ns 31.77 ns 1.53 3.94 KB 1.35
Moq 10 19,486.9 ns 106.44 ns 94.36 ns 17.80 17.03 KB 5.86
NSubstitute 10 18,559.7 ns 76.51 ns 71.57 ns 16.95 21.08 KB 7.25
FakeItEasy 10 20,377.2 ns 127.68 ns 119.43 ns 18.61 30.81 KB 10.60
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.400
[Host] : .NET 10.0.11 (10.0.11, 10.0.1126.37416), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

CreateMock Mean Error StdDev Ratio Allocated Alloc Ratio
baseline* 14.46 ns 0.085 ns 0.079 ns 0.77 160 B 1.00
Mockolate 18.67 ns 0.288 ns 0.269 ns 1.00 160 B 1.00
Imposter 304.17 ns 6.323 ns 5.914 ns 16.29 2248 B 14.05
TUnitMocks 38.72 ns 0.272 ns 0.255 ns 2.07 200 B 1.25
Moq 1,433.01 ns 5.097 ns 4.767 ns 76.76 2096 B 13.10
NSubstitute 2,007.36 ns 25.526 ns 23.877 ns 107.53 5048 B 31.55
FakeItEasy 1,846.23 ns 17.037 ns 15.103 ns 98.90 2763 B 17.27

baseline* rows show the corresponding Mockolate benchmark from the most recent successful main branch build with results, for regression comparison.

Copilot AI review requested due to automatic review settings August 26, 2026 19:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@sonarqubecloud

Copy link
Copy Markdown

@vbreuss
vbreuss merged commit 0e57c9e into main Aug 26, 2026
18 checks passed
@vbreuss
vbreuss deleted the fix/sibling-interface-wrapping branch August 26, 2026 19:12
github-actions Bot added a commit that referenced this pull request Aug 26, 2026
…e members via the declaring interface (#849) by Valentin Breuß
github-actions Bot added a commit that referenced this pull request Aug 26, 2026
…e members via the declaring interface (#849) by Valentin Breuß
@github-actions

Copy link
Copy Markdown

This is addressed in release v3.4.1.

@github-actions github-actions Bot added the state: released The issue is released label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working state: released The issue is released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants