A generated mock advertised setup and verification for both accessors of
every property, even when it intercepted only one. For a get-only property
int RemainingBars { get; }
`Setup.RemainingBars.OnSet` and `Verify.RemainingBars.Set(3)` compiled and
silently did nothing: no setter body is emitted, so a write can never be
recorded and the verification could only ever report zero.
The generator now picks the facade from the accessors it actually emits a
body for:
- IPropertyGetterOnlySetup<T> / IPropertySetterOnlySetup<T> replace
PropertySetup<T> on the setup surface.
- VerificationPropertyGetterResult<TSubject> and
VerificationPropertySetterResult<TSubject, TParameter> replace
VerificationPropertyResult<TSubject, TParameter> on the verify surface,
each taking only the member id of the accessor it exposes.
GetInterceptedAccessors is the single classification behind all three
decisions, keyed on the same Getter/Setter nullity that guards body
emission, so the surface cannot drift from what the proxy intercepts.
This also completes the cross-assembly case from #828: a property such as
`{ get; internal set; }` whose setter is invisible to the mock's assembly
now narrows to the getter instead of offering a write that never arrives.
BREAKING CHANGE: on a property the mock reads or writes but not both,
`Setup.X.OnSet`/`OnGet`, `Setup.X.Returns`/`Throws`/`InitializeWith` and
`Verify.X.Set(...)`/`Got()` no longer compile for the absent accessor.
Each previously compiled and silently had no effect. The library API is
additive; only generated mock surfaces narrow.
The restriction covers the property's own setup surface. The fluent
builders returned by Returns/Throws/Do/TransitionTo are shared with
read-write properties, so chaining past one of them reaches the full setup
again; the verify facades have no such continuation and are fully
restricted. Indexers and init-only properties keep the full surface.
A generated mock advertised setup and verification for both accessors of every property, even when it intercepted only one. For a get-only property
Setup.RemainingBars.OnSetandVerify.RemainingBars.Set(3)compiled and silently did nothing: no setter body is emitted, so a write can never be recorded and the verification could only ever report zero.The generator now picks the facade from the accessors it actually emits a body for:
GetInterceptedAccessors is the single classification behind all three decisions, keyed on the same Getter/Setter nullity that guards body emission, so the surface cannot drift from what the proxy intercepts.
This also completes the cross-assembly case from #828: a property such as
{ get; internal set; }whose setter is invisible to the mock's assembly now narrows to the getter instead of offering a write that never arrives.BREAKING CHANGE: on a property the mock reads or writes but not both,
Setup.X.OnSet/OnGet,Setup.X.Returns/Throws/InitializeWithandVerify.X.Set(...)/Got()no longer compile for the absent accessor. Each previously compiled and silently had no effect. The library API is additive; only generated mock surfaces narrow.The restriction survives chaining: the narrowed setup facades have their own fluent builder hierarchies (
IPropertyGetterOnlySetupReturnBuilder<T>,IPropertyGetterOnlySetupCallbackBuilder<T>,IPropertySetterOnlySetupCallbackBuilder<T>and their When/Parallel stages, including Forever/OnlyOnce), so chaining past Returns/Throws/Do/TransitionTo can never reach the accessor the mock does not intercept. The verify facades have no continuation and are likewise fully restricted. Indexers and init-only properties keep the full surface.