From 1447aa09a30fdb4f096a18103af47f6ed596aff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 29 Jul 2026 01:00:36 +0200 Subject: [PATCH 1/2] feat!: only expose property accessors the mock intercepts 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 / IPropertySetterOnlySetup replace PropertySetup on the setup surface. - VerificationPropertyGetterResult and VerificationPropertySetterResult replace VerificationPropertyResult 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. --- Docs/pages/08-analyzers.md | 5 +- Docs/pages/setup/01-properties.md | 49 ++++++++++ .../Entities/PropertyAccessors.cs | 8 ++ .../Sources/Sources.MockClass.cs | 74 ++++++++++++-- .../Setup/Interfaces.PropertySetup.cs | 70 ++++++++++++++ Source/Mockolate/Setup/PropertySetup.cs | 45 ++++++++- .../Verify/VerificationPropertyResult.cs | 83 ++++++++++++++++ .../Expected/Mockolate_net10.0.txt | 34 ++++++- .../Expected/Mockolate_net8.0.txt | 34 ++++++- .../Expected/Mockolate_netstandard2.0.txt | 34 ++++++- .../MockTests.ClassTests.PropertiesTests.cs | 42 ++++++++ .../MockTests.CrossAssemblyTests.cs | 7 ++ .../MockTests.cs | 20 ++-- ...nsiveAbstractClass__ICombinationMockA.g.cs | 6 +- ..._ICombinationMockA__ICombinationMockB.g.cs | 12 +-- .../Mock.ICombinationMockA.g.cs | 16 ++-- .../Mock.ICombinationMockB.g.cs | 16 ++-- .../Mock.IComprehensiveInterface.g.cs | 42 ++++---- .../Mock.IKeywordEdgeCases.g.cs | 16 ++-- .../Mock.IStaticAbstractMembers.g.cs | 10 +- ...upPropertyTests.AccessorRestrictedTests.cs | 96 +++++++++++++++++++ 21 files changed, 635 insertions(+), 84 deletions(-) create mode 100644 Source/Mockolate.SourceGenerators/Entities/PropertyAccessors.cs create mode 100644 Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs diff --git a/Docs/pages/08-analyzers.md b/Docs/pages/08-analyzers.md index b8c75bb6..64c39a78 100644 --- a/Docs/pages/08-analyzers.md +++ b/Docs/pages/08-analyzers.md @@ -66,7 +66,10 @@ implementing it, so the rule still fires. The same applies per accessor. For a property whose accessors differ in accessibility, such as `public abstract string Flavour { get; internal set; }`, an override further down discharges only the inaccessible half. The mock then overrides the accessor it can see and leaves the other one to the -referenced assembly's implementation, so writes through the mock are not intercepted or recorded. +referenced assembly's implementation. Because writes never reach the mock, `Setup` and `Verify` expose +only the getter for such a property, so a write that could never be recorded is not offered for +configuration or verification. See +[properties with only one accessor](setup/properties#properties-with-only-one-accessor) for details. ## Mockolate0003 diff --git a/Docs/pages/setup/01-properties.md b/Docs/pages/setup/01-properties.md index fffa47b7..7c401ccd 100644 --- a/Docs/pages/setup/01-properties.md +++ b/Docs/pages/setup/01-properties.md @@ -72,6 +72,55 @@ sut.Mock.Setup.TotalDispensed.OnGet .Do(() => Console.WriteLine("Execute on all odd read interactions")); ``` +## Properties with only one accessor + +The setup and verify surfaces only offer the accessors the mock actually intercepts. +`Register()` and `SkippingBaseClass(…)` stay available on both, but the accessor-specific members do +not: a get-only property has no `OnSet`, and a set-only property has neither `OnGet` nor the +`Returns`/`Throws` read-sequence nor `InitializeWith`, since there is no getter to read the value +back. + +```csharp +public interface IChocolateInventory +{ + int RemainingBars { get; } // no setter + string LastCountedBy { set; } // no getter +} + +IChocolateInventory sut = IChocolateInventory.CreateMock(); + +sut.Mock.Setup.RemainingBars.Returns(3); +sut.Mock.Setup.RemainingBars.Register(); +sut.Mock.Setup.RemainingBars.OnSet… // does not compile + +sut.Mock.Setup.LastCountedBy.OnSet.Do(value => { }); +sut.Mock.Setup.LastCountedBy.Register(); +sut.Mock.Setup.LastCountedBy.Returns("Ada")… // does not compile +sut.Mock.Setup.LastCountedBy.InitializeWith("Ada")… // does not compile +``` + +The verify facade likewise offers the intercepted accessor only: + +```csharp +_ = sut.RemainingBars; +sut.LastCountedBy = "Ada"; + +await That(sut.Mock.Verify.RemainingBars.Got()).Once(); +await That(sut.Mock.Verify.LastCountedBy.Set("Ada")).Once(); + +sut.Mock.Verify.RemainingBars.Set(3)… // does not compile +sut.Mock.Verify.LastCountedBy.Got()… // does not compile +``` + +This also applies when the property declares an accessor the mock cannot see, such as +`{ get; internal set; }` on a type from an assembly that does not grant `InternalsVisibleTo`. Writes +never reach the mock in that case, so configuring or verifying one could only ever report zero +interactions. See [Mockolate0002](../analyzers#mockolate0002) for when such a type is mockable at all. + +The verify facade is fully restricted. On the setup side the restriction covers the property's own +surface: the fluent builders returned by `Returns`, `Throws`, `Do` and `TransitionTo` are shared with +read-write properties, so chaining on past one of them reaches the full setup again. + **Notes:** - Use `.SkippingBaseClass(…)` to override the base class behavior for a specific property (only for class mocks). diff --git a/Source/Mockolate.SourceGenerators/Entities/PropertyAccessors.cs b/Source/Mockolate.SourceGenerators/Entities/PropertyAccessors.cs new file mode 100644 index 00000000..65eaaf08 --- /dev/null +++ b/Source/Mockolate.SourceGenerators/Entities/PropertyAccessors.cs @@ -0,0 +1,8 @@ +namespace Mockolate.SourceGenerators.Entities; + +internal enum PropertyAccessors +{ + Both, + GetOnly, + SetOnly, +} diff --git a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs index 9be05474..af310f73 100644 --- a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs +++ b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs @@ -3142,6 +3142,54 @@ private static bool ParametersBlockAllValuesPromotion(EquatableArray + /// Which accessors of the mock actually intercepts, and therefore + /// which setup/verify facade the generated surface exposes. The setup type, the verify type and the + /// verify constructor arguments all derive from this one classification so they cannot disagree. + /// + /// + /// The classification follows the accessors the mock emits a body for, which is exactly what + /// / being non-null means: the property + /// declares the accessor, and it is not an internal/private protected one hidden from + /// the mock's assembly. Other accessibilities are not considered here, so a + /// { get; private set; } property still classifies as . + /// The arm therefore also absorbs degenerate shapes that + /// cannot produce compiling output regardless of the facade chosen. + /// + private static PropertyAccessors GetInterceptedAccessors(Property property) + => (property.Getter, property.Setter) switch + { + (not null, null) => PropertyAccessors.GetOnly, + (null, not null) => PropertyAccessors.SetOnly, + _ => PropertyAccessors.Both, + }; + + /// + /// The setup facade emitted for , narrowed to the accessors classified + /// by . + /// + private static string GetPropertySetupType(Property property) + => GetInterceptedAccessors(property) switch + { + PropertyAccessors.GetOnly => $"global::Mockolate.Setup.IPropertyGetterOnlySetup<{property.Type.Fullname}>", + PropertyAccessors.SetOnly => $"global::Mockolate.Setup.IPropertySetterOnlySetup<{property.Type.Fullname}>", + _ => $"global::Mockolate.Setup.PropertySetup<{property.Type.Fullname}>", + }; + + /// + /// The verify facade emitted for , narrowed to the accessors classified + /// by . + /// + private static string GetPropertyVerifyType(Property property, string verifyName) + => GetInterceptedAccessors(property) switch + { + PropertyAccessors.GetOnly => + $"global::Mockolate.Verify.VerificationPropertyGetterResult<{verifyName}>", + PropertyAccessors.SetOnly => + $"global::Mockolate.Verify.VerificationPropertySetterResult<{verifyName}, {property.Type.Fullname}>", + _ => $"global::Mockolate.Verify.VerificationPropertyResult<{verifyName}, {property.Type.Fullname}>", + }; + private static void DefineSetupInterface(StringBuilder sb, Class @class, MemberType memberType, bool hasOverloadResolutionPriority) { @@ -3154,7 +3202,7 @@ private static void DefineSetupInterface(StringBuilder sb, Class @class, MemberT { sb.AppendXmlSummary( $"Setup for the {property.Type.Fullname.EscapeForXmlDoc()} property ."); - sb.Append("\t\tglobal::Mockolate.Setup.PropertySetup<").Append(property.Type.Fullname).Append("> ") + sb.Append("\t\t").Append(GetPropertySetupType(property)).Append(' ') .Append(property.Name).Append(" { get; }").AppendLine(); sb.AppendLine(); } @@ -3591,8 +3639,8 @@ private static void ImplementSetupInterface(StringBuilder sb, Class @class, stri sb.Append( "\t\t[global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)]") .AppendLine(); - sb.Append("\t\tglobal::Mockolate.Setup.PropertySetup<").Append(property.Type.Fullname) - .Append("> global::Mockolate.Mock.").Append(setupName).Append('.').Append(property.Name).AppendLine(); + sb.Append("\t\t").Append(GetPropertySetupType(property)) + .Append(" global::Mockolate.Mock.").Append(setupName).Append('.').Append(property.Name).AppendLine(); sb.Append("\t\t{").AppendLine(); sb.Append("\t\t\tget").AppendLine(); sb.Append("\t\t\t{").AppendLine(); @@ -5013,8 +5061,8 @@ private static void DefineVerifyInterface(StringBuilder sb, Class @class, string { sb.AppendXmlSummary( $"Verify interactions with the {property.Type.Fullname.EscapeForXmlDoc()} property ."); - sb.Append("\t\tglobal::Mockolate.Verify.VerificationPropertyResult<").Append(verifyName).Append(", ") - .Append(property.Type.Fullname).Append("> ").Append(property.Name).Append(" { get; }").AppendLine(); + sb.Append("\t\t").Append(GetPropertyVerifyType(property, verifyName)).Append(' ') + .Append(property.Name).Append(" { get; }").AppendLine(); sb.AppendLine(); } @@ -5287,15 +5335,21 @@ private static void ImplementVerifyInterface(StringBuilder sb, Class @class, str sb.Append( "\t\t[global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)]") .AppendLine(); - sb.Append("\t\tglobal::Mockolate.Verify.VerificationPropertyResult<").Append(verifyName).Append(", ") - .Append(property.Type.Fullname).Append("> ").Append(verifyName).Append('.').Append(property.Name) + string verifyType = GetPropertyVerifyType(property, verifyName); + // The narrow views take only the member id of the accessor they expose. + string verifyMemberIds = GetInterceptedAccessors(property) switch + { + PropertyAccessors.GetOnly => propertyGetMemberId, + PropertyAccessors.SetOnly => propertySetMemberId, + _ => $"{propertyGetMemberId}, {propertySetMemberId}", + }; + sb.Append("\t\t").Append(verifyType).Append(' ').Append(verifyName).Append('.').Append(property.Name) .AppendLine(); sb.Append("\t\t{").AppendLine(); sb.Append("\t\t\tget").AppendLine(); sb.Append("\t\t\t{").AppendLine(); - sb.Append("\t\t\t\treturn new global::Mockolate.Verify.VerificationPropertyResult<").Append(verifyName) - .Append(", ").Append(property.Type.Fullname).Append(">(this, this.").Append(mockRegistryName) - .Append(", ").Append(propertyGetMemberId).Append(", ").Append(propertySetMemberId).Append(", ") + sb.Append("\t\t\t\treturn new ").Append(verifyType).Append("(this, this.").Append(mockRegistryName) + .Append(", ").Append(verifyMemberIds).Append(", ") .Append(property.GetUniqueNameString()).Append(");").AppendLine(); sb.Append("\t\t\t}").AppendLine(); sb.Append("\t\t}").AppendLine(); diff --git a/Source/Mockolate/Setup/Interfaces.PropertySetup.cs b/Source/Mockolate/Setup/Interfaces.PropertySetup.cs index 6f997f8c..2f46779b 100644 --- a/Source/Mockolate/Setup/Interfaces.PropertySetup.cs +++ b/Source/Mockolate/Setup/Interfaces.PropertySetup.cs @@ -274,6 +274,76 @@ IPropertySetupReturnBuilder Throws() IPropertySetupReturnBuilder Throws(Func callback); } +/// +/// Setup for a mocked property of type that the mock only reads. +/// +/// +/// Used instead of when the mock has no setter to intercept, either +/// because the property is declared without one or because its setter is not accessible from the +/// mock's assembly. Writes then never reach the mock, so is not +/// offered. +/// +public interface IPropertyGetterOnlySetup +{ + /// + IPropertyGetterSetup OnGet { get; } + + /// + IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + + /// + IPropertyGetterOnlySetup Register(); + + /// + /// + /// Seeds the value that reads return. Unlike a read-write property there is no setter to update the + /// slot afterwards, so it stays at unless a Returns entry applies. + /// + IPropertyGetterOnlySetup InitializeWith(T value); + + /// + IPropertySetupReturnBuilder Returns(T returnValue); + + /// + IPropertySetupReturnBuilder Returns(Func callback); + + /// + IPropertySetupReturnBuilder Returns(Func callback); + + /// + IPropertySetupReturnBuilder Throws() + where TException : Exception, new(); + + /// + IPropertySetupReturnBuilder Throws(Exception exception); + + /// + IPropertySetupReturnBuilder Throws(Func callback); + + /// + IPropertySetupReturnBuilder Throws(Func callback); +} + +/// +/// Setup for a mocked property of type that the mock only writes. +/// +/// +/// The write-only counterpart of : the mock has no getter to +/// intercept, so and the Returns/Throws +/// read-sequence are not offered. +/// +public interface IPropertySetterOnlySetup +{ + /// + IPropertySetterSetup OnSet { get; } + + /// + IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + + /// + IPropertySetterOnlySetup Register(); +} + /// /// Interface for setting up a property getter with fluent syntax. /// diff --git a/Source/Mockolate/Setup/PropertySetup.cs b/Source/Mockolate/Setup/PropertySetup.cs index 6962fae3..89a36c2e 100644 --- a/Source/Mockolate/Setup/PropertySetup.cs +++ b/Source/Mockolate/Setup/PropertySetup.cs @@ -189,7 +189,8 @@ internal override TResult InvokeGetterFast(MockBehavior behavior, public class PropertySetup : PropertySetup, IPropertyGetterSetupCallbackBuilder, IPropertySetterSetupCallbackBuilder, IPropertySetupReturnBuilder, - IPropertyGetterSetup, IPropertySetterSetup + IPropertyGetterSetup, IPropertySetterSetup, + IPropertyGetterOnlySetup, IPropertySetterOnlySetup { private readonly MockRegistry _mockRegistry; private readonly string _name; @@ -668,4 +669,46 @@ T Delegate(int _, T p) } #endregion IPropertySetup + + #region Accessor-restricted views + + // The narrow views reuse the full implementation and only re-type the chaining return value, so a + // get-only property cannot reach OnSet and a set-only one cannot reach OnGet or the read-sequence. + + /// + IPropertyGetterOnlySetup IPropertyGetterOnlySetup.SkippingBaseClass(bool skipBaseClass) + { + SkippingBaseClass(skipBaseClass); + return this; + } + + /// + IPropertyGetterOnlySetup IPropertyGetterOnlySetup.Register() + { + Register(); + return this; + } + + /// + IPropertyGetterOnlySetup IPropertyGetterOnlySetup.InitializeWith(T value) + { + InitializeWith(value); + return this; + } + + /// + IPropertySetterOnlySetup IPropertySetterOnlySetup.SkippingBaseClass(bool skipBaseClass) + { + SkippingBaseClass(skipBaseClass); + return this; + } + + /// + IPropertySetterOnlySetup IPropertySetterOnlySetup.Register() + { + Register(); + return this; + } + + #endregion Accessor-restricted views } diff --git a/Source/Mockolate/Verify/VerificationPropertyResult.cs b/Source/Mockolate/Verify/VerificationPropertyResult.cs index fff7d0f4..bdffb6bd 100644 --- a/Source/Mockolate/Verify/VerificationPropertyResult.cs +++ b/Source/Mockolate/Verify/VerificationPropertyResult.cs @@ -65,3 +65,86 @@ public VerificationResult Set(TParameter value, => _mockRegistry.VerifyPropertyTyped(_subject, _setMemberId, _propertyName, It.Is(value, doNotPopulateThisValue).AsParameterMatch()); } + +/// +/// Verifications on a property that the mock only reads. +/// +/// +/// Used instead of when the mock has no +/// setter to intercept, either because the property is declared without one or because its setter is +/// not accessible from the mock's assembly. Writes then never reach the mock, so offering +/// Set(...) here would always report zero interactions. The property type is not a type +/// parameter because only Set(...) needs it. +/// +#if !DEBUG +[System.Diagnostics.DebuggerNonUserCode] +#endif +public class VerificationPropertyGetterResult +{ + private readonly int _getMemberId; + private readonly MockRegistry _mockRegistry; + private readonly string _propertyName; + private readonly TSubject _subject; + + /// + /// The verification facade the result is bound to. + /// The mock registry holding the recorded interactions. + /// Member id of the property getter, or -1 when unknown. + /// The simple property name. + public VerificationPropertyGetterResult(TSubject subject, MockRegistry mockRegistry, int getMemberId, + string propertyName) + { + _subject = subject; + _mockRegistry = mockRegistry; + _getMemberId = getMemberId; + _propertyName = propertyName; + } + + /// + public VerificationResult Got() + => _mockRegistry.VerifyPropertyTyped(_subject, _getMemberId, _propertyName); +} + +/// +/// Verifications on a property of type that the mock only writes. +/// +/// +/// The write-only counterpart of : the mock +/// has no getter to intercept, so Got() is not offered. +/// +#if !DEBUG +[System.Diagnostics.DebuggerNonUserCode] +#endif +public class VerificationPropertySetterResult +{ + private readonly MockRegistry _mockRegistry; + private readonly string _propertyName; + private readonly int _setMemberId; + private readonly TSubject _subject; + + /// + /// The verification facade the result is bound to. + /// The mock registry holding the recorded interactions. + /// Member id of the property setter, or -1 when unknown. + /// The simple property name. + public VerificationPropertySetterResult(TSubject subject, MockRegistry mockRegistry, int setMemberId, + string propertyName) + { + _subject = subject; + _mockRegistry = mockRegistry; + _setMemberId = setMemberId; + _propertyName = propertyName; + } + + /// + public VerificationResult Set(IParameter value) + => _mockRegistry.VerifyPropertyTyped(_subject, _setMemberId, _propertyName, value.AsParameterMatch()); + + /// + [OverloadResolutionPriority(1)] + public VerificationResult Set(TParameter value, + [CallerArgumentExpression(nameof(value))] + string doNotPopulateThisValue = "") + => _mockRegistry.VerifyPropertyTyped(_subject, _setMemberId, _propertyName, + It.Is(value, doNotPopulateThisValue).AsParameterMatch()); +} diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt index f82bbcb1..64de411a 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt @@ -1472,6 +1472,21 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); + Mockolate.Setup.IPropertyGetterOnlySetup Register(); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws() + where TException : System.Exception, new (); + } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder InParallel(); @@ -1492,6 +1507,12 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetup Register(); + Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + } public interface IPropertySetterSetupCallbackBuilder : Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder InParallel(); @@ -2308,7 +2329,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } @@ -2889,6 +2910,11 @@ namespace Mockolate.Verify public override Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public override Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertyGetterResult + { + public VerificationPropertyGetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int getMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Got() { } + } public class VerificationPropertyResult { public VerificationPropertyResult(TSubject subject, Mockolate.MockRegistry mockRegistry, string propertyName) { } @@ -2897,6 +2923,12 @@ namespace Mockolate.Verify public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertySetterResult + { + public VerificationPropertySetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int setMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } + public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } + } public static class VerificationResultExtensions { extension(Mockolate.Verify.VerificationResult? verificationResult) diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt index dac64b74..77c20302 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt @@ -1430,6 +1430,21 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); + Mockolate.Setup.IPropertyGetterOnlySetup Register(); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws() + where TException : System.Exception, new (); + } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder InParallel(); @@ -1450,6 +1465,12 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetup Register(); + Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + } public interface IPropertySetterSetupCallbackBuilder : Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder InParallel(); @@ -2070,7 +2091,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } @@ -2443,6 +2464,11 @@ namespace Mockolate.Verify public override Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public override Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertyGetterResult + { + public VerificationPropertyGetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int getMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Got() { } + } public class VerificationPropertyResult { public VerificationPropertyResult(TSubject subject, Mockolate.MockRegistry mockRegistry, string propertyName) { } @@ -2451,6 +2477,12 @@ namespace Mockolate.Verify public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertySetterResult + { + public VerificationPropertySetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int setMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } + public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } + } public static class VerificationResultExtensions { extension(Mockolate.Verify.VerificationResult? verificationResult) diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt index 945ebdd9..6cc2c267 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt @@ -1373,6 +1373,21 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); + Mockolate.Setup.IPropertyGetterOnlySetup Register(); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertySetupReturnBuilder Throws() + where TException : System.Exception, new (); + } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder InParallel(); @@ -1393,6 +1408,12 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetup Register(); + Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); + } public interface IPropertySetterSetupCallbackBuilder : Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup { Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder InParallel(); @@ -2013,7 +2034,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } @@ -2372,6 +2393,11 @@ namespace Mockolate.Verify public override Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public override Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertyGetterResult + { + public VerificationPropertyGetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int getMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Got() { } + } public class VerificationPropertyResult { public VerificationPropertyResult(TSubject subject, Mockolate.MockRegistry mockRegistry, string propertyName) { } @@ -2380,6 +2406,12 @@ namespace Mockolate.Verify public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } } + public class VerificationPropertySetterResult + { + public VerificationPropertySetterResult(TSubject subject, Mockolate.MockRegistry mockRegistry, int setMemberId, string propertyName) { } + public Mockolate.Verify.VerificationResult Set(Mockolate.Parameters.IParameter value) { } + public Mockolate.Verify.VerificationResult Set(TParameter value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { } + } public static class VerificationResultExtensions { extension(Mockolate.Verify.VerificationResult? verificationResult) diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs index c51f297a..b4a66576 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs @@ -6,6 +6,48 @@ public sealed partial class ClassTests { public sealed class PropertiesTests { + [Theory] + [InlineData("string Name { get; }", "Setup.IPropertyGetterOnlySetup", + "Verify.VerificationPropertyGetterResult", + "Setup.PropertySetup", "Verify.VerificationPropertyResult")] + [InlineData("string Name { set; }", "Setup.IPropertySetterOnlySetup", + "Verify.VerificationPropertySetterResult", + "Setup.PropertySetup", "Verify.VerificationPropertyResult")] + [InlineData("string Name { get; set; }", "Setup.PropertySetup", + "Verify.VerificationPropertyResult", + "Setup.IPropertyGetterOnlySetup", "Verify.VerificationPropertyGetterResult")] + public async Task PropertySurface_ShouldOnlyExposeTheAccessorsTheMockIntercepts( + string property, string expectedSetupType, string expectedVerifyType, + string unexpectedSetupType, string unexpectedVerifyType) + { + GeneratorResult result = Generator + .Run($$""" + using Mockolate; + + namespace MyCode; + public class Program + { + public static void Main(string[] args) + { + _ = IMyService.CreateMock(); + } + } + + public interface IMyService + { + {{property}} + } + """); + + await That(result.Diagnostics).IsEmpty(); + await That(result.Sources["Mock.IMyService.g.cs"]) + .Contains($"global::Mockolate.{expectedSetupType} Name {{ get; }}").And + .Contains($"global::Mockolate.{expectedVerifyType} Name {{ get; }}").And + .DoesNotContain($"global::Mockolate.{unexpectedSetupType} Name {{ get; }}").And + .DoesNotContain($"global::Mockolate.{unexpectedVerifyType} Name {{ get; }}") + .Because("a widened facade would silently re-expose an accessor the mock never intercepts"); + } + [Fact] public async Task InitOnlyProperty_ShouldEmitInitAccessorAndCompile() { diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CrossAssemblyTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CrossAssemblyTests.cs index 7c71111d..8a0f0be7 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CrossAssemblyTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CrossAssemblyTests.cs @@ -299,6 +299,9 @@ public abstract class MyExternalType : MyBaseType await That(result.Diagnostics).IsEmpty(); await That(result.Sources["Mock.MyExternalType.g.cs"]) + .Contains("global::Mockolate.Setup.IPropertyGetterOnlySetup Mixed { get; }").And + .Contains("global::Mockolate.Verify.VerificationPropertyGetterResult Mixed { get; }") + .Because("the mock does not intercept the inaccessible setter, so neither surface may offer it").And .Contains("public override string Mixed").And .Contains(".GetProperty").And .DoesNotContain(inaccessibleAccessor) @@ -329,6 +332,10 @@ public abstract class MyExternalType : MyBaseType await That(result.Diagnostics).IsEmpty(); await That(result.Sources["Mock.MyExternalType.g.cs"]) + .Contains("global::Mockolate.Setup.IPropertySetterOnlySetup Mixed { get; }").And + .Contains( + "global::Mockolate.Verify.VerificationPropertySetterResult Mixed { get; }") + .Because("the mock does not intercept the inaccessible getter, so neither surface may offer it").And .Contains("public override string Mixed").And .Contains(".SetProperty").And .DoesNotContain(inaccessibleAccessor) diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs index 25babde0..4087f049 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs @@ -67,11 +67,11 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.OuterClass.g.cs"); await That(result.Sources["Mock.OuterClass.g.cs"]) - .Contains("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.OuterValue").And - .Contains("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.BaseClassValue").And - .DoesNotContain("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.DirectValue").And - .DoesNotContain("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.ParentValue").And - .DoesNotContain("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.NestedValue").And + .Contains("global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForOuterClass.OuterValue").And + .Contains("global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForOuterClass.BaseClassValue").And + .DoesNotContain("IMockSetupForOuterClass.DirectValue").And + .DoesNotContain("IMockSetupForOuterClass.ParentValue").And + .DoesNotContain("IMockSetupForOuterClass.NestedValue").And .Contains("global::Mockolate.Setup.IReturnMethodSetup global::Mockolate.Mock.IMockSetupForOuterClass.OuterMethod()").And .Contains("global::Mockolate.Setup.IReturnMethodSetup global::Mockolate.Mock.IMockSetupForOuterClass.BaseClassMethod()").And .DoesNotContain("global::Mockolate.Setup.IReturnMethodSetup global::Mockolate.Mock.IMockSetupForOuterClass.DirectMethod()").And @@ -82,11 +82,11 @@ await That(result.Sources["Mock.OuterClass.g.cs"]) .DoesNotContain("void IMockRaiseOnOuterClass.DirectEvent(object? sender, global::System.EventArgs e)").And .DoesNotContain("void IMockRaiseOnOuterClass.ParentEvent(object? sender, global::System.EventArgs e)").And .DoesNotContain("void IMockRaiseOnOuterClass.NestedEvent(object? sender, global::System.EventArgs e)").And - .Contains("global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForOuterClass.OuterValue").And - .Contains("global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForOuterClass.BaseClassValue").And - .DoesNotContain("global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForOuterClass.DirectValue").And - .DoesNotContain("global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForOuterClass.ParentValue").And - .DoesNotContain("global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForOuterClass.NestedValue").And + .Contains("global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForOuterClass.OuterValue").And + .Contains("global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForOuterClass.BaseClassValue").And + .DoesNotContain("IMockVerifyForOuterClass.DirectValue").And + .DoesNotContain("IMockVerifyForOuterClass.ParentValue").And + .DoesNotContain("IMockVerifyForOuterClass.NestedValue").And .Contains("global::Mockolate.Verify.VerificationResult.IgnoreParameters IMockVerifyForOuterClass.OuterMethod()").And .Contains("global::Mockolate.Verify.VerificationResult.IgnoreParameters IMockVerifyForOuterClass.BaseClassMethod()").And .DoesNotContain("IMockVerifyForOuterClass.DirectMethod()").And diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA.g.cs index 1d96ce08..64329f76 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA.g.cs @@ -415,7 +415,7 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value { get { @@ -463,11 +463,11 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockA.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockA.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, -1, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); } } diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA__ICombinationMockB.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA__ICombinationMockB.g.cs index 4dc71c33..eba1dd31 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA__ICombinationMockB.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ComprehensiveAbstractClass__ICombinationMockA__ICombinationMockB.g.cs @@ -523,7 +523,7 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value { get { @@ -547,7 +547,7 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value { get { @@ -595,11 +595,11 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockA.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockA.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, -1, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); } } @@ -612,11 +612,11 @@ protected override int P() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockB.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockB.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, -1, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, -1, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); } } diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockA.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockA.g.cs index 0022e9bf..c3358747 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockA.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockA.g.cs @@ -179,7 +179,7 @@ public void Run() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value { get { @@ -203,11 +203,11 @@ public void Run() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockA.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockA.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Get, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Set, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Get, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); } } @@ -226,11 +226,11 @@ private sealed class VerifyMonitorICombinationMockA(global::Mockolate.MockRegist /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockA.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockA.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Get, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Set, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockA.MemberId_Value_Get, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockA.Value"); } } @@ -260,7 +260,7 @@ public MockInScenarioForICombinationMockA(global::Mockolate.MockRegistry mockReg /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockA.Value { get { @@ -404,7 +404,7 @@ internal interface IMockSetupForICombinationMockA : global::Mockolate.Setup.IMoc /// /// Setup for the int property Value. /// - global::Mockolate.Setup.PropertySetup Value { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup Value { get; } /// /// Setup for the method Run(). @@ -422,7 +422,7 @@ internal interface IMockVerifyForICombinationMockA : global::Mockolate.Verify.IM /// /// Verify interactions with the int property Value. /// - global::Mockolate.Verify.VerificationPropertyResult Value { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult Value { get; } /// /// Verify invocations for the method Run(). diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockB.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockB.g.cs index 935f23c5..0dc5a39b 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockB.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/BaseClass_WithMultipleAdditionalInterfaces_CanBeCreated/Mock.ICombinationMockB.g.cs @@ -179,7 +179,7 @@ public void Run() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value { get { @@ -203,11 +203,11 @@ public void Run() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockB.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockB.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Get, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Set, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Get, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); } } @@ -226,11 +226,11 @@ private sealed class VerifyMonitorICombinationMockB(global::Mockolate.MockRegist /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForICombinationMockB.Value + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForICombinationMockB.Value { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Get, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Set, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.ICombinationMockB.MemberId_Value_Get, "global::Mockolate.Tests.GeneratorCoverage.ICombinationMockB.Value"); } } @@ -260,7 +260,7 @@ public MockInScenarioForICombinationMockB(global::Mockolate.MockRegistry mockReg /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForICombinationMockB.Value { get { @@ -404,7 +404,7 @@ internal interface IMockSetupForICombinationMockB : global::Mockolate.Setup.IMoc /// /// Setup for the int property Value. /// - global::Mockolate.Setup.PropertySetup Value { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup Value { get; } /// /// Setup for the method Run(). @@ -422,7 +422,7 @@ internal interface IMockVerifyForICombinationMockB : global::Mockolate.Verify.IM /// /// Verify interactions with the int property Value. /// - global::Mockolate.Verify.VerificationPropertyResult Value { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult Value { get; } /// /// Verify invocations for the method Run(). diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated/Mock.IComprehensiveInterface.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated/Mock.IComprehensiveInterface.g.cs index c073699c..4dec103f 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated/Mock.IComprehensiveInterface.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated/Mock.IComprehensiveInterface.g.cs @@ -2204,7 +2204,7 @@ public void SeventeenVoid(int a1, int a2, int a3, int a4, int a5, int a6, int a7 /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.GetOnly + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.GetOnly { get { @@ -2216,7 +2216,7 @@ public void SeventeenVoid(int a1, int a2, int a3, int a4, int a5, int a6, int a7 /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.SetOnly + global::Mockolate.Setup.IPropertySetterOnlySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.SetOnly { get { @@ -2804,7 +2804,7 @@ public void SeventeenVoid(int a1, int a2, int a3, int a4, int a5, int a6, int a7 /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockStaticSetupForIComprehensiveInterface.StaticAbstractValue + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockStaticSetupForIComprehensiveInterface.StaticAbstractValue { get { @@ -2886,21 +2886,21 @@ void IMockRaiseOnIComprehensiveInterface.CustomEvent(global::Mockolate.Parameter /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIComprehensiveInterface.GetOnly + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForIComprehensiveInterface.GetOnly { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Get, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.GetOnly"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Get, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.GetOnly"); } } /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIComprehensiveInterface.SetOnly + global::Mockolate.Verify.VerificationPropertySetterResult IMockVerifyForIComprehensiveInterface.SetOnly { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Get, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.SetOnly"); + return new global::Mockolate.Verify.VerificationPropertySetterResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.SetOnly"); } } @@ -3383,11 +3383,11 @@ void IMockRaiseOnIComprehensiveInterface.CustomEvent(global::Mockolate.Parameter /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockStaticVerifyForIComprehensiveInterface.StaticAbstractValue + global::Mockolate.Verify.VerificationPropertyGetterResult IMockStaticVerifyForIComprehensiveInterface.StaticAbstractValue { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, -1, -1, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.StaticAbstractValue"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, -1, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.StaticAbstractValue"); } } @@ -3416,21 +3416,21 @@ private sealed class VerifyMonitorIComprehensiveInterface(global::Mockolate.Mock /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIComprehensiveInterface.GetOnly + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForIComprehensiveInterface.GetOnly { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Get, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.GetOnly"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_GetOnly_Get, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.GetOnly"); } } /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIComprehensiveInterface.SetOnly + global::Mockolate.Verify.VerificationPropertySetterResult IMockVerifyForIComprehensiveInterface.SetOnly { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Get, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.SetOnly"); + return new global::Mockolate.Verify.VerificationPropertySetterResult(this, this.MockRegistry, global::Mockolate.Mock.IComprehensiveInterface.MemberId_SetOnly_Set, "global::Mockolate.Tests.GeneratorCoverage.IComprehensiveInterface.SetOnly"); } } @@ -3942,7 +3942,7 @@ public MockInScenarioForIComprehensiveInterface(global::Mockolate.MockRegistry m /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.GetOnly + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.GetOnly { get { @@ -3954,7 +3954,7 @@ public MockInScenarioForIComprehensiveInterface(global::Mockolate.MockRegistry m /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.SetOnly + global::Mockolate.Setup.IPropertySetterOnlySetup global::Mockolate.Mock.IMockSetupForIComprehensiveInterface.SetOnly { get { @@ -4691,12 +4691,12 @@ internal interface IMockSetupForIComprehensiveInterface /// /// Setup for the int property GetOnly. /// - global::Mockolate.Setup.PropertySetup GetOnly { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup GetOnly { get; } /// /// Setup for the int property SetOnly. /// - global::Mockolate.Setup.PropertySetup SetOnly { get; } + global::Mockolate.Setup.IPropertySetterOnlySetup SetOnly { get; } /// /// Setup for the string? property NullableProp. @@ -5250,7 +5250,7 @@ internal interface IMockStaticSetupForIComprehensiveInterface /// /// Setup for the int property StaticAbstractValue. /// - global::Mockolate.Setup.PropertySetup StaticAbstractValue { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup StaticAbstractValue { get; } /// /// Setup for the method StaticAbstractMethod(). @@ -5310,12 +5310,12 @@ internal interface IMockVerifyForIComprehensiveInterface /// /// Verify interactions with the int property GetOnly. /// - global::Mockolate.Verify.VerificationPropertyResult GetOnly { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult GetOnly { get; } /// /// Verify interactions with the int property SetOnly. /// - global::Mockolate.Verify.VerificationPropertyResult SetOnly { get; } + global::Mockolate.Verify.VerificationPropertySetterResult SetOnly { get; } /// /// Verify interactions with the string? property NullableProp. @@ -5860,7 +5860,7 @@ internal interface IMockStaticVerifyForIComprehensiveInterface /// /// Verify interactions with the int property StaticAbstractValue. /// - global::Mockolate.Verify.VerificationPropertyResult StaticAbstractValue { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult StaticAbstractValue { get; } /// /// Verify invocations for the method StaticAbstractMethod(). diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated/Mock.IKeywordEdgeCases.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated/Mock.IKeywordEdgeCases.g.cs index 2d6faf64..7c537db5 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated/Mock.IKeywordEdgeCases.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated/Mock.IKeywordEdgeCases.g.cs @@ -415,7 +415,7 @@ public int @void<@class>(int @ref) /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIKeywordEdgeCases.@class + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForIKeywordEdgeCases.@class { get { @@ -567,11 +567,11 @@ void IMockRaiseOnIKeywordEdgeCases.@event(global::Mockolate.Parameters.IDefaultE /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIKeywordEdgeCases.@class + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForIKeywordEdgeCases.@class { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Get, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Set, "global::Mockolate.Tests.GeneratorCoverage.IKeywordEdgeCases.@class"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Get, "global::Mockolate.Tests.GeneratorCoverage.IKeywordEdgeCases.@class"); } } @@ -687,11 +687,11 @@ private sealed class VerifyMonitorIKeywordEdgeCases(global::Mockolate.MockRegist /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockVerifyForIKeywordEdgeCases.@class + global::Mockolate.Verify.VerificationPropertyGetterResult IMockVerifyForIKeywordEdgeCases.@class { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Get, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Set, "global::Mockolate.Tests.GeneratorCoverage.IKeywordEdgeCases.@class"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, global::Mockolate.Mock.IKeywordEdgeCases.MemberId__class_Get, "global::Mockolate.Tests.GeneratorCoverage.IKeywordEdgeCases.@class"); } } @@ -818,7 +818,7 @@ public MockInScenarioForIKeywordEdgeCases(global::Mockolate.MockRegistry mockReg /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForIKeywordEdgeCases.@class + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockSetupForIKeywordEdgeCases.@class { get { @@ -1081,7 +1081,7 @@ internal interface IMockSetupForIKeywordEdgeCases : global::Mockolate.Setup.IMoc /// /// Setup for the int property @class. /// - global::Mockolate.Setup.PropertySetup @class { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup @class { get; } /// /// Setup for the event @event. @@ -1211,7 +1211,7 @@ internal interface IMockVerifyForIKeywordEdgeCases : global::Mockolate.Verify.IM /// /// Verify interactions with the int property @class. /// - global::Mockolate.Verify.VerificationPropertyResult @class { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult @class { get; } /// /// Verify interactions with the string indexer this[int, string]. diff --git a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/StaticAbstractMembers_CanBeCreated/Mock.IStaticAbstractMembers.g.cs b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/StaticAbstractMembers_CanBeCreated/Mock.IStaticAbstractMembers.g.cs index c442eac0..2f1452a5 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/StaticAbstractMembers_CanBeCreated/Mock.IStaticAbstractMembers.g.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/StaticAbstractMembers_CanBeCreated/Mock.IStaticAbstractMembers.g.cs @@ -304,7 +304,7 @@ public static int VirtualStaticMethod() /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockStaticSetupForIStaticAbstractMembers.VirtualStaticProperty + global::Mockolate.Setup.IPropertyGetterOnlySetup global::Mockolate.Mock.IMockStaticSetupForIStaticAbstractMembers.VirtualStaticProperty { get { @@ -379,11 +379,11 @@ void IMockStaticRaiseOnIStaticAbstractMembers.AbstractStaticEvent(global::Mockol /// [global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)] - global::Mockolate.Verify.VerificationPropertyResult IMockStaticVerifyForIStaticAbstractMembers.VirtualStaticProperty + global::Mockolate.Verify.VerificationPropertyGetterResult IMockStaticVerifyForIStaticAbstractMembers.VirtualStaticProperty { get { - return new global::Mockolate.Verify.VerificationPropertyResult(this, this.MockRegistry, -1, -1, "global::Mockolate.Tests.GeneratorCoverage.IStaticAbstractMembers.VirtualStaticProperty"); + return new global::Mockolate.Verify.VerificationPropertyGetterResult(this, this.MockRegistry, -1, "global::Mockolate.Tests.GeneratorCoverage.IStaticAbstractMembers.VirtualStaticProperty"); } } @@ -598,7 +598,7 @@ internal interface IMockStaticSetupForIStaticAbstractMembers /// /// Setup for the int property VirtualStaticProperty. /// - global::Mockolate.Setup.PropertySetup VirtualStaticProperty { get; } + global::Mockolate.Setup.IPropertyGetterOnlySetup VirtualStaticProperty { get; } /// /// Setup for the event AbstractStaticEvent. @@ -656,7 +656,7 @@ internal interface IMockStaticVerifyForIStaticAbstractMembers /// /// Verify interactions with the int property VirtualStaticProperty. /// - global::Mockolate.Verify.VerificationPropertyResult VirtualStaticProperty { get; } + global::Mockolate.Verify.VerificationPropertyGetterResult VirtualStaticProperty { get; } /// /// Verify invocations for the method AbstractStaticMethod(). diff --git a/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs b/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs new file mode 100644 index 00000000..9d8a04e2 --- /dev/null +++ b/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; + +namespace Mockolate.Tests.MockProperties; + +public sealed partial class SetupPropertyTests +{ + public sealed class AccessorRestrictedTests + { + [Fact] + public async Task SetOnlyProperty_OnSet_ShouldFireAndRecordTheWrite() + { + IAccessorService sut = IAccessorService.CreateMock(); + List written = new(); + sut.Mock.Setup.WriteOnly.OnSet.Do(value => written.Add(value)); + + sut.WriteOnly = "Ada"; + + await That(written).IsEqualTo(["Ada",]); + await That(sut.Mock.Verify.WriteOnly.Set("Ada")).Once() + .Because("the setter facade must be keyed on the setter member id, not the getter's"); + } + + [Fact] + public async Task SetOnlyProperty_VerifyOtherValue_ShouldNotMatch() + { + IAccessorService sut = IAccessorService.CreateMock(); + + sut.WriteOnly = "Ada"; + + await That(sut.Mock.Verify.WriteOnly.Set("Grace")).Never(); + } + + [Fact] + public async Task SetOnlyProperty_VerifyWithParameterMatcher_ShouldMatch() + { + IAccessorService sut = IAccessorService.CreateMock(); + + sut.WriteOnly = "Ada"; + + await That(sut.Mock.Verify.WriteOnly.Set(It.IsAny())).Once(); + } + + [Fact] + public async Task SetOnlyProperty_Register_ShouldAllowWriteWithoutSetup() + { + IAccessorService sut = IAccessorService.CreateMock(MockBehavior.Default.ThrowingWhenNotSetup()); + sut.Mock.Setup.WriteOnly.Register(); + + void Act() => sut.WriteOnly = "Ada"; + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task GetOnlyProperty_Register_ShouldAllowReadWithoutSetup() + { + IAccessorService sut = IAccessorService.CreateMock(MockBehavior.Default.ThrowingWhenNotSetup()); + sut.Mock.Setup.ReadOnly.Register(); + + int result = sut.ReadOnly; + + await That(result).IsEqualTo(0); + await That(sut.Mock.Verify.ReadOnly.Got()).Once(); + } + + [Theory] + [InlineData(false, 1)] + [InlineData(true, 0)] + public async Task SetOnlyClassProperty_ShouldSkipCallingBaseWhenRequested(bool skipBaseClass, + int expectedCallCount) + { + AccessorService sut = AccessorService.CreateMock(); + sut.Mock.Setup.WriteOnly.SkippingBaseClass(skipBaseClass); + + sut.WriteOnly = 1; + + await That(sut.WriteOnlySetterCallCount).IsEqualTo(expectedCallCount); + } + + public interface IAccessorService + { + int ReadOnly { get; } + string WriteOnly { set; } + } + + public class AccessorService + { + public int WriteOnlySetterCallCount { get; private set; } + + public virtual int WriteOnly + { + set => WriteOnlySetterCallCount += value; + } + } + } +} From 3cb6e4675f67bd31665e07b71ee5cd4d972de2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 29 Jul 2026 10:59:18 +0200 Subject: [PATCH 2/2] feat: keep accessor-restricted property setups narrowed through chaining The fluent builders returned by Returns, Throws, Do and TransitionTo on IPropertyGetterOnlySetup/IPropertySetterOnlySetup previously extended the full IPropertySetup, so one chaining hop re-exposed the accessor the mock never intercepts. Parallel getter-only and setter-only builder hierarchies now keep the whole chain on the narrowed surface, with Forever/OnlyOnce extensions for the narrowed when-builders. --- .../CompleteEventBenchmarks.cs | 2 +- Docs/pages/setup/01-properties.md | 8 +- .../Setup/Interfaces.PropertySetup.cs | 145 +++++++++- Source/Mockolate/Setup/PropertySetup.cs | 200 +++++++++++++- Source/Mockolate/SetupExtensions.cs | 59 ++++ .../Expected/Mockolate_net10.0.txt | 82 +++++- .../Expected/Mockolate_net8.0.txt | 82 +++++- .../Expected/Mockolate_netstandard2.0.txt | 82 +++++- ...upPropertyTests.AccessorRestrictedTests.cs | 258 ++++++++++++++++++ 9 files changed, 871 insertions(+), 47 deletions(-) diff --git a/Benchmarks/Mockolate.Benchmarks/CompleteEventBenchmarks.cs b/Benchmarks/Mockolate.Benchmarks/CompleteEventBenchmarks.cs index 786f6283..66818be1 100644 --- a/Benchmarks/Mockolate.Benchmarks/CompleteEventBenchmarks.cs +++ b/Benchmarks/Mockolate.Benchmarks/CompleteEventBenchmarks.cs @@ -89,7 +89,7 @@ public void Event_NSubstitute() EventHandler handler = (_, _) => { }; mock.SomeEvent += handler; - mock.SomeEvent += Raise.EventWith(null, EventArgs.Empty); + mock.SomeEvent += Raise.EventWith(null!, EventArgs.Empty); mock.Received(1).SomeEvent += Arg.Any(); } diff --git a/Docs/pages/setup/01-properties.md b/Docs/pages/setup/01-properties.md index 7c401ccd..632a03e5 100644 --- a/Docs/pages/setup/01-properties.md +++ b/Docs/pages/setup/01-properties.md @@ -92,11 +92,13 @@ IChocolateInventory sut = IChocolateInventory.CreateMock(); sut.Mock.Setup.RemainingBars.Returns(3); sut.Mock.Setup.RemainingBars.Register(); sut.Mock.Setup.RemainingBars.OnSet… // does not compile +sut.Mock.Setup.RemainingBars.Returns(3).OnSet… // does not compile sut.Mock.Setup.LastCountedBy.OnSet.Do(value => { }); sut.Mock.Setup.LastCountedBy.Register(); sut.Mock.Setup.LastCountedBy.Returns("Ada")… // does not compile sut.Mock.Setup.LastCountedBy.InitializeWith("Ada")… // does not compile +sut.Mock.Setup.LastCountedBy.OnSet.Do(value => { }).OnGet… // does not compile ``` The verify facade likewise offers the intercepted accessor only: @@ -117,9 +119,9 @@ This also applies when the property declares an accessor the mock cannot see, su never reach the mock in that case, so configuring or verifying one could only ever report zero interactions. See [Mockolate0002](../analyzers#mockolate0002) for when such a type is mockable at all. -The verify facade is fully restricted. On the setup side the restriction covers the property's own -surface: the fluent builders returned by `Returns`, `Throws`, `Do` and `TransitionTo` are shared with -read-write properties, so chaining on past one of them reaches the full setup again. +Both facades are fully restricted: the fluent builders returned by `Returns`, `Throws`, `Do` and +`TransitionTo` stay on the narrowed surface, so no amount of chaining reaches the accessor the mock +does not intercept. **Notes:** diff --git a/Source/Mockolate/Setup/Interfaces.PropertySetup.cs b/Source/Mockolate/Setup/Interfaces.PropertySetup.cs index 2f46779b..3316c1f2 100644 --- a/Source/Mockolate/Setup/Interfaces.PropertySetup.cs +++ b/Source/Mockolate/Setup/Interfaces.PropertySetup.cs @@ -286,7 +286,7 @@ IPropertySetupReturnBuilder Throws() public interface IPropertyGetterOnlySetup { /// - IPropertyGetterSetup OnGet { get; } + IPropertyGetterOnlyGetterSetup OnGet { get; } /// IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); @@ -302,26 +302,26 @@ public interface IPropertyGetterOnlySetup IPropertyGetterOnlySetup InitializeWith(T value); /// - IPropertySetupReturnBuilder Returns(T returnValue); + IPropertyGetterOnlySetupReturnBuilder Returns(T returnValue); /// - IPropertySetupReturnBuilder Returns(Func callback); + IPropertyGetterOnlySetupReturnBuilder Returns(Func callback); /// - IPropertySetupReturnBuilder Returns(Func callback); + IPropertyGetterOnlySetupReturnBuilder Returns(Func callback); /// - IPropertySetupReturnBuilder Throws() + IPropertyGetterOnlySetupReturnBuilder Throws() where TException : Exception, new(); /// - IPropertySetupReturnBuilder Throws(Exception exception); + IPropertyGetterOnlySetupReturnBuilder Throws(Exception exception); /// - IPropertySetupReturnBuilder Throws(Func callback); + IPropertyGetterOnlySetupReturnBuilder Throws(Func callback); /// - IPropertySetupReturnBuilder Throws(Func callback); + IPropertyGetterOnlySetupReturnBuilder Throws(Func callback); } /// @@ -335,7 +335,7 @@ IPropertySetupReturnBuilder Throws() public interface IPropertySetterOnlySetup { /// - IPropertySetterSetup OnSet { get; } + IPropertySetterOnlySetterSetup OnSet { get; } /// IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); @@ -499,3 +499,130 @@ public interface IPropertySetupReturnWhenBuilder : IPropertySetup /// The outer setup for chaining additional returns/throws. IPropertySetup Only(int times); } + +/// +/// Setup for attaching side-effects to the getter of a property the mock only reads. +/// +/// +/// The counterpart of for : +/// the returned builders stay on the getter-only surface, so chaining can never reach +/// . +/// +public interface IPropertyGetterOnlyGetterSetup +{ + /// + IPropertyGetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertyGetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertyGetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertyGetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); +} + +/// +/// Interface for setting up the getter of a get-only property with fluent syntax. +/// +public interface IPropertyGetterOnlySetupParallelCallbackBuilder : IPropertyGetterOnlySetupCallbackWhenBuilder +{ + /// + IPropertyGetterOnlySetupCallbackWhenBuilder When(Func predicate); +} + +/// +/// Interface for setting up the getter of a get-only property with fluent syntax. +/// +public interface IPropertyGetterOnlySetupCallbackBuilder : IPropertyGetterOnlySetupParallelCallbackBuilder +{ + /// + IPropertyGetterOnlySetupParallelCallbackBuilder InParallel(); +} + +/// +/// Interface for setting up the getter of a get-only property with fluent syntax. +/// +public interface IPropertyGetterOnlySetupCallbackWhenBuilder : IPropertyGetterOnlySetup +{ + /// + IPropertyGetterOnlySetupCallbackWhenBuilder For(int times); + + /// + IPropertyGetterOnlySetup Only(int times); +} + +/// +/// Setup for attaching side-effects to the setter of a property the mock only writes. +/// +/// +/// The counterpart of for : +/// the returned builders stay on the setter-only surface, so chaining can never reach +/// or the Returns/Throws read-sequence. +/// +public interface IPropertySetterOnlySetterSetup +{ + /// + IPropertySetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertySetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertySetterOnlySetupCallbackBuilder Do(Action callback); + + /// + IPropertySetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); +} + +/// +/// Interface for setting up the setter of a set-only property with fluent syntax. +/// +public interface IPropertySetterOnlySetupParallelCallbackBuilder : IPropertySetterOnlySetupCallbackWhenBuilder +{ + /// + IPropertySetterOnlySetupCallbackWhenBuilder When(Func predicate); +} + +/// +/// Interface for setting up the setter of a set-only property with fluent syntax. +/// +public interface IPropertySetterOnlySetupCallbackBuilder : IPropertySetterOnlySetupParallelCallbackBuilder +{ + /// + IPropertySetterOnlySetupParallelCallbackBuilder InParallel(); +} + +/// +/// Interface for setting up the setter of a set-only property with fluent syntax. +/// +public interface IPropertySetterOnlySetupCallbackWhenBuilder : IPropertySetterOnlySetup +{ + /// + IPropertySetterOnlySetupCallbackWhenBuilder For(int times); + + /// + IPropertySetterOnlySetup Only(int times); +} + +/// +/// Interface for setting up a return/throw builder for a get-only property with fluent syntax. +/// +public interface IPropertyGetterOnlySetupReturnBuilder : IPropertyGetterOnlySetupReturnWhenBuilder +{ + /// + IPropertyGetterOnlySetupReturnWhenBuilder When(Func predicate); +} + +/// +/// Interface for setting up a when builder for returns/throws for a get-only property with fluent syntax. +/// +public interface IPropertyGetterOnlySetupReturnWhenBuilder : IPropertyGetterOnlySetup +{ + /// + IPropertyGetterOnlySetupReturnWhenBuilder For(int times); + + /// + IPropertyGetterOnlySetup Only(int times); +} diff --git a/Source/Mockolate/Setup/PropertySetup.cs b/Source/Mockolate/Setup/PropertySetup.cs index 89a36c2e..84bedfeb 100644 --- a/Source/Mockolate/Setup/PropertySetup.cs +++ b/Source/Mockolate/Setup/PropertySetup.cs @@ -190,7 +190,10 @@ public class PropertySetup : PropertySetup, IPropertyGetterSetupCallbackBuilder, IPropertySetterSetupCallbackBuilder, IPropertySetupReturnBuilder, IPropertyGetterSetup, IPropertySetterSetup, - IPropertyGetterOnlySetup, IPropertySetterOnlySetup + IPropertyGetterOnlySetup, IPropertySetterOnlySetup, + IPropertyGetterOnlyGetterSetup, IPropertySetterOnlySetterSetup, + IPropertyGetterOnlySetupCallbackBuilder, IPropertySetterOnlySetupCallbackBuilder, + IPropertyGetterOnlySetupReturnBuilder { private readonly MockRegistry _mockRegistry; private readonly string _name; @@ -672,9 +675,6 @@ T Delegate(int _, T p) #region Accessor-restricted views - // The narrow views reuse the full implementation and only re-type the chaining return value, so a - // get-only property cannot reach OnSet and a set-only one cannot reach OnGet or the read-sequence. - /// IPropertyGetterOnlySetup IPropertyGetterOnlySetup.SkippingBaseClass(bool skipBaseClass) { @@ -696,6 +696,137 @@ IPropertyGetterOnlySetup IPropertyGetterOnlySetup.InitializeWith(T value) return this; } + /// + IPropertyGetterOnlyGetterSetup IPropertyGetterOnlySetup.OnGet + => this; + + /// + IPropertyGetterOnlySetupCallbackBuilder IPropertyGetterOnlyGetterSetup.Do(Action callback) + { + ((IPropertyGetterSetup)this).Do(callback); + return this; + } + + /// + IPropertyGetterOnlySetupCallbackBuilder IPropertyGetterOnlyGetterSetup.Do(Action callback) + { + ((IPropertyGetterSetup)this).Do(callback); + return this; + } + + /// + IPropertyGetterOnlySetupCallbackBuilder IPropertyGetterOnlyGetterSetup.Do(Action callback) + { + ((IPropertyGetterSetup)this).Do(callback); + return this; + } + + /// + IPropertyGetterOnlySetupParallelCallbackBuilder IPropertyGetterOnlyGetterSetup.TransitionTo(string scenario) + { + ((IPropertyGetterSetup)this).TransitionTo(scenario); + return this; + } + + /// + IPropertyGetterOnlySetupParallelCallbackBuilder IPropertyGetterOnlySetupCallbackBuilder.InParallel() + { + ((IPropertyGetterSetupCallbackBuilder)this).InParallel(); + return this; + } + + /// + IPropertyGetterOnlySetupCallbackWhenBuilder IPropertyGetterOnlySetupParallelCallbackBuilder.When( + Func predicate) + { + ((IPropertyGetterSetupParallelCallbackBuilder)this).When(predicate); + return this; + } + + /// + IPropertyGetterOnlySetupCallbackWhenBuilder IPropertyGetterOnlySetupCallbackWhenBuilder.For(int times) + { + ((IPropertyGetterSetupCallbackWhenBuilder)this).For(times); + return this; + } + + /// + IPropertyGetterOnlySetup IPropertyGetterOnlySetupCallbackWhenBuilder.Only(int times) + { + ((IPropertyGetterSetupCallbackWhenBuilder)this).Only(times); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Returns(T returnValue) + { + Returns(returnValue); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Returns(Func callback) + { + Returns(callback); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Returns(Func callback) + { + Returns(callback); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Throws() + { + Throws(); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Throws(Exception exception) + { + Throws(exception); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Throws(Func callback) + { + Throws(callback); + return this; + } + + /// + IPropertyGetterOnlySetupReturnBuilder IPropertyGetterOnlySetup.Throws(Func callback) + { + Throws(callback); + return this; + } + + /// + IPropertyGetterOnlySetupReturnWhenBuilder IPropertyGetterOnlySetupReturnBuilder.When(Func predicate) + { + ((IPropertySetupReturnBuilder)this).When(predicate); + return this; + } + + /// + IPropertyGetterOnlySetupReturnWhenBuilder IPropertyGetterOnlySetupReturnWhenBuilder.For(int times) + { + ((IPropertySetupReturnWhenBuilder)this).For(times); + return this; + } + + /// + IPropertyGetterOnlySetup IPropertyGetterOnlySetupReturnWhenBuilder.Only(int times) + { + ((IPropertySetupReturnWhenBuilder)this).Only(times); + return this; + } + /// IPropertySetterOnlySetup IPropertySetterOnlySetup.SkippingBaseClass(bool skipBaseClass) { @@ -710,5 +841,66 @@ IPropertySetterOnlySetup IPropertySetterOnlySetup.Register() return this; } + /// + IPropertySetterOnlySetterSetup IPropertySetterOnlySetup.OnSet + => this; + + /// + IPropertySetterOnlySetupCallbackBuilder IPropertySetterOnlySetterSetup.Do(Action callback) + { + ((IPropertySetterSetup)this).Do(callback); + return this; + } + + /// + IPropertySetterOnlySetupCallbackBuilder IPropertySetterOnlySetterSetup.Do(Action callback) + { + ((IPropertySetterSetup)this).Do(callback); + return this; + } + + /// + IPropertySetterOnlySetupCallbackBuilder IPropertySetterOnlySetterSetup.Do(Action callback) + { + ((IPropertySetterSetup)this).Do(callback); + return this; + } + + /// + IPropertySetterOnlySetupParallelCallbackBuilder IPropertySetterOnlySetterSetup.TransitionTo(string scenario) + { + ((IPropertySetterSetup)this).TransitionTo(scenario); + return this; + } + + /// + IPropertySetterOnlySetupParallelCallbackBuilder IPropertySetterOnlySetupCallbackBuilder.InParallel() + { + ((IPropertySetterSetupCallbackBuilder)this).InParallel(); + return this; + } + + /// + IPropertySetterOnlySetupCallbackWhenBuilder IPropertySetterOnlySetupParallelCallbackBuilder.When( + Func predicate) + { + ((IPropertySetterSetupParallelCallbackBuilder)this).When(predicate); + return this; + } + + /// + IPropertySetterOnlySetupCallbackWhenBuilder IPropertySetterOnlySetupCallbackWhenBuilder.For(int times) + { + ((IPropertySetterSetupCallbackWhenBuilder)this).For(times); + return this; + } + + /// + IPropertySetterOnlySetup IPropertySetterOnlySetupCallbackWhenBuilder.Only(int times) + { + ((IPropertySetterSetupCallbackWhenBuilder)this).Only(times); + return this; + } + #endregion Accessor-restricted views } diff --git a/Source/Mockolate/SetupExtensions.cs b/Source/Mockolate/SetupExtensions.cs index b6f842af..ca5430c3 100644 --- a/Source/Mockolate/SetupExtensions.cs +++ b/Source/Mockolate/SetupExtensions.cs @@ -69,6 +69,65 @@ public IPropertySetup OnlyOnce() => setup.Only(1); } + /// + /// Extensions for setups of get-only properties. + /// + extension(IPropertyGetterOnlySetupReturnWhenBuilder setup) + { + /// + /// Terminates the return/throw sequence by repeating the preceding entry forever instead of cycling + /// back to the first entry once the end is reached. + /// + /// + /// Equivalent to .For(int.MaxValue). Applies only to the preceding Returns(...)/Throws(...) + /// entry; earlier entries in the sequence still run once each in order. + /// + public void Forever() + => setup.For(int.MaxValue); + + /// + /// Deactivates the preceding Returns(...)/Throws(...) entry after a single invocation, + /// so subsequent invocations fall through to the next sequence entry (or to the mock's default behaviour). + /// + /// + /// Equivalent to .Only(1). + /// + public IPropertyGetterOnlySetup OnlyOnce() + => setup.Only(1); + } + + /// + /// Extensions for getter callback setups of get-only properties. + /// + extension(IPropertyGetterOnlySetupCallbackWhenBuilder setup) + { + /// + /// Deactivates the preceding Do(...) callback after a single invocation, so subsequent invocations + /// fall through to the next callback in the sequence (or are skipped). + /// + /// + /// Equivalent to .Only(1). + /// + public IPropertyGetterOnlySetup OnlyOnce() + => setup.Only(1); + } + + /// + /// Extensions for setter callback setups of set-only properties. + /// + extension(IPropertySetterOnlySetupCallbackWhenBuilder setup) + { + /// + /// Deactivates the preceding Do(...) callback after a single invocation, so subsequent invocations + /// fall through to the next callback in the sequence (or are skipped). + /// + /// + /// Equivalent to .Only(1). + /// + public IPropertySetterOnlySetup OnlyOnce() + => setup.Only(1); + } + /// /// Extensions for event subscription callback setups. /// diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt index 64de411a..06425391 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt @@ -382,6 +382,19 @@ namespace Mockolate { public Mockolate.Setup.IPropertySetup OnlyOnce() { } } + extension(Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder? setup) + { + public void Forever() { } + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertySetterOnlySetup OnlyOnce() { } + } extension(Mockolate.Setup.IEventSubscriptionSetupCallbackWhenBuilder? setup) { public void Forever() { } @@ -1472,19 +1485,48 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlyGetterSetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertyGetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertyGetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } + public interface IPropertyGetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnBuilder : Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } public interface IPropertyGetterOnlySetup { - Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlyGetterSetup OnGet { get; } Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); Mockolate.Setup.IPropertyGetterOnlySetup Register(); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(T returnValue); Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws() + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws() where TException : System.Exception, new (); } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup @@ -1507,9 +1549,29 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetterSetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertySetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertySetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertySetterOnlySetup Only(int times); + } + public interface IPropertySetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } public interface IPropertySetterOnlySetup { - Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetterSetup OnSet { get; } Mockolate.Setup.IPropertySetterOnlySetup Register(); Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); } @@ -2329,7 +2391,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlyGetterSetup, Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetterSetup, Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt index 77c20302..9db3d608 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt @@ -365,6 +365,19 @@ namespace Mockolate { public Mockolate.Setup.IPropertySetup OnlyOnce() { } } + extension(Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder? setup) + { + public void Forever() { } + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertySetterOnlySetup OnlyOnce() { } + } extension(Mockolate.Setup.IEventSubscriptionSetupCallbackWhenBuilder? setup) { public void Forever() { } @@ -1430,19 +1443,48 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlyGetterSetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertyGetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertyGetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } + public interface IPropertyGetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnBuilder : Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } public interface IPropertyGetterOnlySetup { - Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlyGetterSetup OnGet { get; } Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); Mockolate.Setup.IPropertyGetterOnlySetup Register(); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(T returnValue); Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws() + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws() where TException : System.Exception, new (); } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup @@ -1465,9 +1507,29 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetterSetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertySetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertySetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertySetterOnlySetup Only(int times); + } + public interface IPropertySetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } public interface IPropertySetterOnlySetup { - Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetterSetup OnSet { get; } Mockolate.Setup.IPropertySetterOnlySetup Register(); Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); } @@ -2091,7 +2153,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlyGetterSetup, Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetterSetup, Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt index 6cc2c267..47d186ea 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt @@ -312,6 +312,19 @@ namespace Mockolate { public Mockolate.Setup.IPropertySetup OnlyOnce() { } } + extension(Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder? setup) + { + public void Forever() { } + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertyGetterOnlySetup OnlyOnce() { } + } + extension(Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder? setup) + { + public Mockolate.Setup.IPropertySetterOnlySetup OnlyOnce() { } + } extension(Mockolate.Setup.IEventSubscriptionSetupCallbackWhenBuilder? setup) { public void Forever() { } @@ -1373,19 +1386,48 @@ namespace Mockolate.Setup string Name { get; } } public interface IMockSetup : Mockolate.IInteractiveMock { } + public interface IPropertyGetterOnlyGetterSetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertyGetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertyGetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } + public interface IPropertyGetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnBuilder : Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder When(System.Func predicate); + } + public interface IPropertyGetterOnlySetupReturnWhenBuilder : Mockolate.Setup.IPropertyGetterOnlySetup + { + Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder For(int times); + Mockolate.Setup.IPropertyGetterOnlySetup Only(int times); + } public interface IPropertyGetterOnlySetup { - Mockolate.Setup.IPropertyGetterSetup OnGet { get; } + Mockolate.Setup.IPropertyGetterOnlyGetterSetup OnGet { get; } Mockolate.Setup.IPropertyGetterOnlySetup InitializeWith(T value); Mockolate.Setup.IPropertyGetterOnlySetup Register(); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Returns(T returnValue); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Returns(T returnValue); Mockolate.Setup.IPropertyGetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Exception exception); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws(System.Func callback); - Mockolate.Setup.IPropertySetupReturnBuilder Throws() + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Exception exception); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws(System.Func callback); + Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder Throws() where TException : System.Exception, new (); } public interface IPropertyGetterSetupCallbackBuilder : Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetup @@ -1408,9 +1450,29 @@ namespace Mockolate.Setup Mockolate.Setup.IPropertyGetterSetupCallbackBuilder Do(System.Action callback); Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder TransitionTo(string scenario); } + public interface IPropertySetterOnlySetterSetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder Do(System.Action callback); + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder TransitionTo(string scenario); + } + public interface IPropertySetterOnlySetupCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder InParallel(); + } + public interface IPropertySetterOnlySetupCallbackWhenBuilder : Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder For(int times); + Mockolate.Setup.IPropertySetterOnlySetup Only(int times); + } + public interface IPropertySetterOnlySetupParallelCallbackBuilder : Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetup + { + Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder When(System.Func predicate); + } public interface IPropertySetterOnlySetup { - Mockolate.Setup.IPropertySetterSetup OnSet { get; } + Mockolate.Setup.IPropertySetterOnlySetterSetup OnSet { get; } Mockolate.Setup.IPropertySetterOnlySetup Register(); Mockolate.Setup.IPropertySetterOnlySetup SkippingBaseClass(bool skipBaseClass = true); } @@ -2034,7 +2096,7 @@ namespace Mockolate.Setup protected abstract void InvokeSetter(TValue value, Mockolate.MockBehavior behavior); protected abstract bool Matches(Mockolate.Interactions.PropertyAccess propertyAccess); } - public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup + public class PropertySetup : Mockolate.Setup.PropertySetup, Mockolate.Setup.IPropertyGetterOnlyGetterSetup, Mockolate.Setup.IPropertyGetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnBuilder, Mockolate.Setup.IPropertyGetterOnlySetupReturnWhenBuilder, Mockolate.Setup.IPropertyGetterOnlySetup, Mockolate.Setup.IPropertyGetterSetupCallbackBuilder, Mockolate.Setup.IPropertyGetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertyGetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertyGetterSetup, Mockolate.Setup.IPropertySetterOnlySetterSetup, Mockolate.Setup.IPropertySetterOnlySetupCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterOnlySetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterOnlySetup, Mockolate.Setup.IPropertySetterSetupCallbackBuilder, Mockolate.Setup.IPropertySetterSetupCallbackWhenBuilder, Mockolate.Setup.IPropertySetterSetupParallelCallbackBuilder, Mockolate.Setup.IPropertySetterSetup, Mockolate.Setup.IPropertySetupReturnBuilder, Mockolate.Setup.IPropertySetupReturnWhenBuilder, Mockolate.Setup.IPropertySetup { public PropertySetup(Mockolate.MockRegistry mockRegistry, string name) { } public override string Name { get; } diff --git a/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs b/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs index 9d8a04e2..03faec94 100644 --- a/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs +++ b/Tests/Mockolate.Tests/MockProperties/SetupPropertyTests.AccessorRestrictedTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Mockolate.Setup; namespace Mockolate.Tests.MockProperties; @@ -77,6 +78,263 @@ public async Task SetOnlyClassProperty_ShouldSkipCallingBaseWhenRequested(bool s await That(sut.WriteOnlySetterCallCount).IsEqualTo(expectedCallCount); } + [Fact] + public async Task GetOnlyProperty_ChainedReturns_ShouldStayOnGetterOnlySurface() + { + IAccessorService sut = IAccessorService.CreateMock(); + IPropertyGetterOnlySetup chained = sut.Mock.Setup.ReadOnly + .Returns(1).OnlyOnce(); + chained.Returns(() => 2); + + await That(sut.ReadOnly).IsEqualTo(1); + await That(sut.ReadOnly).IsEqualTo(2); + } + + [Fact] + public async Task GetOnlyProperty_ReturnsForever_ShouldUseTheLastValueForever() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .Returns(2) + .Returns(4).Forever(); + + int[] result = new int[4]; + for (int i = 0; i < 4; i++) + { + result[i] = sut.ReadOnly; + } + + await That(result).IsEqualTo([2, 4, 4, 4,]); + } + + [Fact] + public async Task GetOnlyProperty_ReturnsWhen_ShouldOnlyUseValueWhenPredicateIsTrue() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .Returns(() => 4).When(i => i > 0); + + int result1 = sut.ReadOnly; + int result2 = sut.ReadOnly; + + await That(result1).IsEqualTo(0); + await That(result2).IsEqualTo(4); + } + + [Fact] + public async Task GetOnlyProperty_ReturnsCallbackWithValue_ShouldReturnExpectedValue() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .InitializeWith(3) + .Returns(x => 4 * x); + + int result = sut.ReadOnly; + + await That(result).IsEqualTo(12); + } + + [Fact] + public async Task GetOnlyProperty_Throws_ShouldIterateThroughAllRegisteredExceptions() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .Throws() + .Throws(new Exception("foo")) + .Throws(() => new Exception("bar")) + .Throws(v => new Exception($"baz-{v}")); + + void Act() => _ = sut.ReadOnly; + + await That(Act).Throws(); + Exception? result2 = Record.Exception(Act); + Exception? result3 = Record.Exception(Act); + Exception? result4 = Record.Exception(Act); + await That(result2).HasMessage("foo"); + await That(result3).HasMessage("bar"); + await That(result4).HasMessage("baz-0"); + } + + [Fact] + public async Task GetOnlyProperty_OnGetChain_ShouldStayOnGetterOnlySurface() + { + int callCount1 = 0; + int callCount2 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + IPropertyGetterOnlySetup chained = sut.Mock.Setup.ReadOnly + .OnGet.Do(() => { callCount1++; }) + .OnGet.Do(v => { callCount2 += 1 + v; }).OnlyOnce(); + chained.Register(); + + _ = sut.ReadOnly; + _ = sut.ReadOnly; + _ = sut.ReadOnly; + _ = sut.ReadOnly; + + await That(callCount1).IsEqualTo(3); + await That(callCount2).IsEqualTo(1); + } + + [Fact] + public async Task GetOnlyProperty_OnGetWhen_ShouldOnlyInvokeCallbackWhenPredicateIsTrue() + { + int callCount = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .OnGet.Do(() => { callCount++; }).When(i => i > 0); + + _ = sut.ReadOnly; + _ = sut.ReadOnly; + _ = sut.ReadOnly; + + await That(callCount).IsEqualTo(2); + } + + [Fact] + public async Task GetOnlyProperty_OnGetInParallel_ShouldInvokeParallelCallbacksAlways() + { + int callCount1 = 0; + int callCount2 = 0; + int callCount3 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .OnGet.Do(() => { callCount1++; }) + .OnGet.Do(v => { callCount2++; }).InParallel() + .OnGet.Do((i, v) => { callCount3++; }); + + _ = sut.ReadOnly; + _ = sut.ReadOnly; + _ = sut.ReadOnly; + _ = sut.ReadOnly; + + await That(callCount1).IsEqualTo(2); + await That(callCount2).IsEqualTo(4); + await That(callCount3).IsEqualTo(2); + } + + [Fact] + public async Task GetOnlyProperty_OnGetFor_ShouldRepeatCallbackTheGivenNumberOfTimes() + { + int callCount1 = 0; + int callCount2 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.ReadOnly + .OnGet.Do(() => { callCount1++; }).For(2) + .OnGet.Do(() => { callCount2++; }); + + for (int i = 0; i < 6; i++) + { + _ = sut.ReadOnly; + } + + await That(callCount1).IsEqualTo(4); + await That(callCount2).IsEqualTo(2); + } + + [Fact] + public async Task GetOnlyProperty_OnGetTransitionTo_ShouldSwitchScenario() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.InScenario("a").Setup.ReadOnly + .OnGet.Do(() => { }) + .OnGet.TransitionTo("b"); + sut.Mock.TransitionTo("a"); + + _ = sut.ReadOnly; + + await That(((IMock)sut).MockRegistry.Scenario).IsEqualTo("b"); + } + + [Fact] + public async Task SetOnlyProperty_OnSetChain_ShouldStayOnSetterOnlySurface() + { + int callCount1 = 0; + int callCount2 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + IPropertySetterOnlySetup chained = sut.Mock.Setup.WriteOnly + .OnSet.Do(() => { callCount1++; }) + .OnSet.Do(v => { callCount2++; }).OnlyOnce(); + chained.Register(); + + sut.WriteOnly = "a"; + sut.WriteOnly = "b"; + sut.WriteOnly = "c"; + sut.WriteOnly = "d"; + + await That(callCount1).IsEqualTo(3); + await That(callCount2).IsEqualTo(1); + } + + [Fact] + public async Task SetOnlyProperty_OnSetWhen_ShouldOnlyInvokeCallbackWhenPredicateIsTrue() + { + int callCount = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.WriteOnly + .OnSet.Do(() => { callCount++; }).When(i => i > 0); + + sut.WriteOnly = "a"; + sut.WriteOnly = "b"; + sut.WriteOnly = "c"; + + await That(callCount).IsEqualTo(2); + } + + [Fact] + public async Task SetOnlyProperty_OnSetInParallel_ShouldInvokeParallelCallbacksAlways() + { + int callCount1 = 0; + int callCount2 = 0; + int callCount3 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.WriteOnly + .OnSet.Do(() => { callCount1++; }) + .OnSet.Do((i, v) => { callCount2++; }).InParallel() + .OnSet.Do(() => { callCount3++; }); + + sut.WriteOnly = "a"; + sut.WriteOnly = "b"; + sut.WriteOnly = "c"; + sut.WriteOnly = "d"; + + await That(callCount1).IsEqualTo(2); + await That(callCount2).IsEqualTo(4); + await That(callCount3).IsEqualTo(2); + } + + [Fact] + public async Task SetOnlyProperty_OnSetFor_ShouldRepeatCallbackTheGivenNumberOfTimes() + { + int callCount1 = 0; + int callCount2 = 0; + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.Setup.WriteOnly + .OnSet.Do(() => { callCount1++; }).For(2) + .OnSet.Do(() => { callCount2++; }); + + for (int i = 0; i < 6; i++) + { + sut.WriteOnly = "a"; + } + + await That(callCount1).IsEqualTo(4); + await That(callCount2).IsEqualTo(2); + } + + [Fact] + public async Task SetOnlyProperty_OnSetTransitionTo_ShouldSwitchScenario() + { + IAccessorService sut = IAccessorService.CreateMock(); + sut.Mock.InScenario("a").Setup.WriteOnly + .OnSet.Do(() => { }) + .OnSet.TransitionTo("b"); + sut.Mock.TransitionTo("a"); + + sut.WriteOnly = "x"; + + await That(((IMock)sut).MockRegistry.Scenario).IsEqualTo("b"); + } + public interface IAccessorService { int ReadOnly { get; }