diff --git a/src/Mimic/Arg.cs b/src/Mimic/Arg.cs index 1f65943..e34cd56 100644 --- a/src/Mimic/Arg.cs +++ b/src/Mimic/Arg.cs @@ -4,17 +4,40 @@ namespace Mimic; +/// +/// Provides argument matchers for use in mock setups and verifications. +/// [PublicAPI] public static class Arg { + /// + /// Matches any value of type , including null. + /// + /// The type of the argument to match. + /// An argument matcher that matches any value of the specified type. public static TValue Any() => typeof(TValue).IsOrContainsGenericMatcher() ? ArgumentMatcher.Create((argument, parameterType) => argument == null || parameterType.IsInstanceOfType(argument))! : ArgumentMatcher.Create(argument => argument == null || argument is TValue)!; + /// + /// Matches any non-null value of type . + /// + /// The type of the argument to match. + /// An argument matcher that matches any non-null value of the specified type. public static TValue AnyNotNull() => typeof(TValue).IsOrContainsGenericMatcher() ? ArgumentMatcher.Create((argument, parameterType) => argument != null && parameterType.IsInstanceOfType(argument))! : ArgumentMatcher.Create(argument => argument is TValue)!; + /// + /// Matches an argument that is equal to the specified value using the default equality comparer. + /// + /// The type of the argument to match. + /// The value to match against. + /// An argument matcher that matches the specified value. + /// + /// This overload does not support generic matchers due to the strongly typed value. + /// For generic matchers, use the overload with Expression<Func<object, Type, bool>> match instead. + /// public static TValue Is(TValue value) { Guard.Assert(!typeof(TValue).IsOrContainsGenericMatcher(), "This overload does not support generic matchers due to the strongly typed value. Please use the overload with `Expression> predicate` instead."); @@ -22,6 +45,17 @@ public static TValue Is(TValue value) return ArgumentMatcher.Create(argument => Equals(argument, value))!; } + /// + /// Matches an argument that is equal to the specified value using the provided equality comparer. + /// + /// The type of the argument to match. + /// The value to match against. + /// The equality comparer to use for comparison. + /// An argument matcher that matches the specified value using the provided comparer. + /// + /// This overload does not support generic matchers due to the strongly typed value. + /// For generic matchers, use the overload with Expression<Func<object, Type, bool>> match instead. + /// public static TValue Is(TValue value, IEqualityComparer comparer) { Guard.Assert(!typeof(TValue).IsOrContainsGenericMatcher(), "This overload does not support generic matchers due to the strongly typed value. Please use the overload with `Expression> predicate` instead."); @@ -29,6 +63,16 @@ public static TValue Is(TValue value, IEqualityComparer comparer return ArgumentMatcher.Create(argument => comparer.Equals(argument, value))!; } + /// + /// Matches an argument that satisfies the specified predicate expression. + /// + /// The type of the argument to match. + /// The predicate expression that the argument must satisfy. + /// An argument matcher that matches arguments satisfying the predicate. + /// + /// This overload does not support generic matchers due to the strongly typed value. + /// For generic matchers, use the overload with Expression<Func<object, Type, bool>> match instead. + /// public static TValue Is(Expression> match) { Guard.Assert(!typeof(TValue).IsOrContainsGenericMatcher(), "This overload does not support generic matchers due to the strongly typed value. Please use the overload with `Expression> predicate` instead."); @@ -36,29 +80,81 @@ public static TValue Is(Expression> match) return ArgumentMatcher.Create(argument => match.Compile().Invoke(argument!))!; } + /// + /// Matches an argument that satisfies the specified predicate expression with type information. + /// This overload supports generic matchers. + /// + /// The type of the argument to match. + /// The predicate expression that takes the argument and its parameter type. + /// An argument matcher that matches arguments satisfying the predicate. public static TValue Is(Expression> match) => ArgumentMatcher.Create((argument, parameterType) => match.Compile().Invoke(argument!, parameterType))!; + /// + /// Matches an argument contained in the specified collection of values. + /// + /// The type of the argument to match. + /// The collection of values to check against. + /// An argument matcher that matches values contained in the collection. public static TValue In(IEnumerable values) => ArgumentMatcher.Create(argument => values.Contains(argument))!; + /// + /// Matches an argument contained in the specified collection of values using the provided equality comparer. + /// + /// The type of the argument to match. + /// The collection of values to check against. + /// The equality comparer to use for comparison. + /// An argument matcher that matches values contained in the collection using the comparer. public static TValue In(IEnumerable values, IEqualityComparer comparer) => ArgumentMatcher.Create(argument => values.Contains(argument, comparer!))!; + /// + /// Matches an argument contained in the specified array of values. + /// + /// The type of the argument to match. + /// The array of values to check against. + /// An argument matcher that matches values contained in the array. public static TValue In(params TValue[] values) => ArgumentMatcher.Create(argument => values.Contains(argument))!; + /// + /// Matches an argument not contained in the specified collection of values. + /// + /// The type of the argument to match. + /// The collection of values to check against. + /// An argument matcher that matches values not contained in the collection. public static TValue NotIn(IEnumerable values) => ArgumentMatcher.Create(argument => !values.Contains(argument))!; + /// + /// Matches an argument not contained in the specified collection of values using the provided equality comparer. + /// + /// The type of the argument to match. + /// The collection of values to check against. + /// The equality comparer to use for comparison. + /// An argument matcher that matches values not contained in the collection using the comparer. public static TValue NotIn(IEnumerable values, IEqualityComparer comparer) => ArgumentMatcher.Create(argument => !values.Contains(argument, comparer!))!; + /// + /// Matches an argument not contained in the specified array of values. + /// + /// The type of the argument to match. + /// The array of values to check against. + /// An argument matcher that matches values not contained in the array. public static TValue NotIn(params TValue[] values) => ArgumentMatcher.Create(argument => !values.Contains(argument))!; + /// + /// Provides reference argument matchers for ref parameters. + /// + /// The type of the reference argument. public static class Ref { + /// + /// Matches any reference argument of type . + /// #pragma warning disable CA2211 public static TValue Any = default!; #pragma warning restore CA2211 diff --git a/src/Mimic/CallCount.cs b/src/Mimic/CallCount.cs index ad57240..2a25820 100644 --- a/src/Mimic/CallCount.cs +++ b/src/Mimic/CallCount.cs @@ -1,5 +1,8 @@ namespace Mimic; +/// +/// Represents a call count constraint that can be used to verify the number of times a method was invoked. +/// [PublicAPI] public readonly struct CallCount : IEquatable { @@ -14,28 +17,62 @@ private CallCount(Type type, int from, int to) _to = to; } + /// + /// Creates a call count constraint that requires at least one invocation. + /// + /// A that validates if a method was called at least once. public static CallCount AtLeastOnce() => new(Type.AtLeastOnce, 1, int.MaxValue); + /// + /// Creates a call count constraint that requires at least the specified number of invocations. + /// + /// The minimum number of invocations required. + /// A that validates if a method was called at least the specified number of times. + /// Thrown when is less than 1. public static CallCount AtLeast(int count) { Guard.Assert(count >= 1); return new CallCount(Type.AtLeast, count, int.MaxValue); } + /// + /// Creates a call count constraint that allows at most the specified number of invocations. + /// + /// The maximum number of invocations allowed. + /// A that validates if a method was called at most the specified number of times. + /// Thrown when is less than 1. public static CallCount AtMost(int count) { Guard.Assert(count >= 1); return new CallCount(Type.AtMost, 0, count); } + /// + /// Creates a call count constraint that allows at most one invocation. + /// + /// A that validates if a method was called at most once. public static CallCount AtMostOnce() => new(Type.AtMostOnce, 0, 1); + /// + /// Creates a call count constraint that requires the number of invocations to be within the specified range (inclusive). + /// + /// The minimum number of invocations (inclusive). + /// The maximum number of invocations (inclusive). + /// A that validates if a method was called within the specified range. + /// Thrown when is negative or is less than . public static CallCount InclusiveBetween(int from, int to) { Guard.Assert(from >= 0 && to >= from); return new CallCount(Type.InclusiveBetween, from, to); } + /// + /// Creates a call count constraint that requires the number of invocations to be within the specified range (exclusive). + /// + /// The minimum number of invocations (exclusive). + /// The maximum number of invocations (exclusive). + /// A that validates if a method was called within the specified range. + /// Thrown when is not positive, is not greater than , or the difference between and is 1. public static CallCount ExclusiveBetween(int from, int to) { Guard.Assert(from > 0 && to > from); @@ -44,16 +81,35 @@ public static CallCount ExclusiveBetween(int from, int to) return new CallCount(Type.ExclusiveBetween, from + 1, to - 1); } + /// + /// Creates a call count constraint that requires exactly the specified number of invocations. + /// + /// The exact number of invocations required. + /// A that validates if a method was called exactly the specified number of times. + /// Thrown when is not positive. public static CallCount Exactly(int count) { Guard.Assert(count > 0); return new CallCount(Type.Exactly, count, count); } + /// + /// Creates a call count constraint that requires exactly one invocation. + /// + /// A that validates if a method was called exactly once. public static CallCount Once() => new(Type.Once, 1, 1); + /// + /// Creates a call count constraint that requires zero invocations. + /// + /// A that validates if a method was never called. public static CallCount Never() => new(Type.Never, 0, 0); + /// + /// Validates whether the specified call count satisfies this constraint. + /// + /// The actual number of invocations to validate. + /// true if the call count satisfies this constraint; otherwise, false. public bool Validate(int count) => _from <= count && count <= _to; public bool Equals(CallCount other) => _from == other._from && _to == other._to; diff --git a/src/Mimic/Generic.cs b/src/Mimic/Generic.cs index 024d2df..d0438e0 100644 --- a/src/Mimic/Generic.cs +++ b/src/Mimic/Generic.cs @@ -1,25 +1,111 @@ namespace Mimic; +/// +/// Provides generic type matchers for use with mock setups and argument matching. +/// These matchers work in conjunction with the class to enable +/// flexible type matching in generic method scenarios. +/// [PublicAPI] public sealed class Generic { + /// + /// A generic type matcher that matches any type without restrictions. + /// This matcher accepts all types, including value types, reference types, + /// interfaces, and abstract classes. + /// + /// + /// Use this matcher when you want to accept any generic type argument in your mock setup. + /// This is equivalent to an unconstrained generic type parameter. + /// + /// + /// + /// // Matches any generic method call regardless of the type argument + /// mimic.Setup(m => m.GenericMethod<Generic.AnyType>()).Returns(true); + /// + /// public sealed class AnyType : IGenericMatcher { + /// + /// Determines whether the specified generic type matches this matcher. + /// + /// The generic type to evaluate. + /// Always returns true since this matcher accepts any type. public bool Matches(Type genericType) => true; } + /// + /// A generic type matcher that matches only reference types. + /// This includes classes, interfaces, delegates, and arrays, but excludes value types like structs and enums. + /// + /// + /// Use this matcher when you want to restrict generic type arguments to reference types only. + /// This is equivalent to a generic type parameter with a class constraint. + /// + /// + /// + /// // Matches generic method calls only when the type argument is a reference type + /// mimic.Setup(m => m.GenericMethod<Generic.AnyReferenceType>()).Returns("reference type"); + /// + /// public sealed class AnyReferenceType : IGenericMatcher { + /// + /// Determines whether the specified generic type matches this matcher. + /// + /// The generic type to evaluate. + /// true if the type is a reference type; otherwise, false. public bool Matches(Type genericType) => !genericType.IsValueType; } + /// + /// A generic type matcher that matches types that are assignable from a specified type . + /// This includes the type itself and any types that inherit from or implement . + /// + /// The base type or interface that the generic type argument must be assignable from. + /// + /// Use this matcher when you want to restrict generic type arguments to types that have a specific inheritance relationship. + /// This is useful for matching generic methods that should only work with certain type hierarchies. + /// + /// + /// + /// // Matches generic method calls only when the type argument implements IDisposable + /// mimic.Setup(m => m.GenericMethod<Generic.AssignableFromType<IDisposable>>()).Returns(true); + /// + /// // Matches generic method calls only when the type argument inherits from Exception + /// mimic.Setup(m => m.HandleException<Generic.AssignableFromType<Exception>>()).Returns("handled"); + /// + /// public sealed class AssignableFromType : IGenericMatcher { + /// + /// Determines whether the specified generic type matches this matcher. + /// + /// The generic type to evaluate. + /// true if is assignable from the generic type; otherwise, false. public bool Matches(Type genericType) => typeof(T).IsAssignableFrom(genericType); } + /// + /// A generic type matcher that matches only value types. + /// This includes structs, enums, and primitive types, but excludes reference types like classes and interfaces. + /// + /// + /// Use this matcher when you want to restrict generic type arguments to value types only. + /// This is equivalent to a generic type parameter with a struct constraint. + /// + /// + /// + /// // Matches generic method calls only when the type argument is a value type + /// mimic.Setup(m => m.GenericMethod<Generic.AnyValueType>()).Returns(42); + /// + /// public readonly struct AnyValueType : IGenericMatcher { + /// + /// Determines whether the specified generic type matches this matcher. + /// + /// The generic type to evaluate. + /// true if the type is a value type; otherwise, false. public bool Matches(Type genericType) => genericType.IsValueType; } } diff --git a/src/Mimic/IGenericMatcher.cs b/src/Mimic/IGenericMatcher.cs index c422171..53effb2 100644 --- a/src/Mimic/IGenericMatcher.cs +++ b/src/Mimic/IGenericMatcher.cs @@ -1,7 +1,82 @@ namespace Mimic; +/// +/// Defines a contract for matching generic types in mock setups and argument matching scenarios. +/// Implementing types can create custom generic type matchers that work with the Mimic framework +/// to provide flexible type matching for generic method calls. +/// +/// +/// +/// This interface is used by the Mimic framework to determine whether a generic type argument +/// matches specific criteria during mock setup and verification. Implementations of this interface +/// can be used as type arguments in generic method setups to create flexible matching rules. +/// +/// +/// The built-in class provides several common implementations of this interface, +/// including matchers for any type, reference types, value types, and types assignable from a base type. +/// Custom implementations can extend this functionality for specific matching requirements. +/// +/// +/// +/// Creating a custom matcher: +/// +/// public class NumericTypeMatcher : IGenericMatcher +/// { +/// public bool Matches(Type genericType) +/// { +/// return genericType == typeof(int) || genericType == typeof(double) || +/// genericType == typeof(decimal) || genericType == typeof(float); +/// } +/// } +/// +/// Using the custom matcher in mock setups (same as built-in Generic matchers): +/// +/// // Setup a generic method to match only numeric types +/// mimic.Setup(m => m.ProcessValue<NumericTypeMatcher>(It.IsAny<NumericTypeMatcher>())) +/// .Returns(true); +/// +/// // This will match calls like: +/// // mock.ProcessValue<int>(42); +/// // mock.ProcessValue<double>(3.14); +/// // But not: +/// // mock.ProcessValue<string>("hello"); +/// +/// Combining with the built-in Generic matchers: +/// +/// // Mix custom and built-in matchers +/// mimic.Setup(m => m.Convert<Generic.AnyReferenceType, NumericTypeMatcher>()) +/// .Returns("converted"); +/// +/// +/// +/// +/// +/// +/// [PublicAPI] public interface IGenericMatcher { + /// + /// Determines whether the specified generic type matches the criteria defined by this matcher. + /// + /// The generic type argument to evaluate for matching. + /// + /// true if the specified satisfies the matching criteria; + /// otherwise, false. + /// + /// + /// This method is called by the Mimic framework during mock setup and verification to determine + /// if a generic type argument matches the expectations defined by the implementing matcher. + /// The implementation should be efficient as it may be called frequently during mock operations. + /// + /// + /// + /// public bool Matches(Type genericType) + /// { + /// // Example: Match only types that implement IComparable + /// return typeof(IComparable).IsAssignableFrom(genericType); + /// } + /// + /// bool Matches(Type genericType); } diff --git a/src/Mimic/IMimicked`1.cs b/src/Mimic/IMimicked`1.cs index a9dfade..5e75fb9 100644 --- a/src/Mimic/IMimicked`1.cs +++ b/src/Mimic/IMimicked`1.cs @@ -1,9 +1,19 @@ namespace Mimic; +/// +/// Represents an interface for accessing the underlying mimic instance for a mocked object. +/// +/// The type being mocked, which must be a reference type. [PublicAPI] [EditorBrowsable(EditorBrowsableState.Never)] public interface IMimicked where T : class { + /// + /// Gets the instance associated with the mocked object. + /// + /// + /// The mimic instance that controls the behaviour and verification of the mocked object. + /// Mimic Mimic { get; } } diff --git a/src/Mimic/Mimic`1.Setup.cs b/src/Mimic/Mimic`1.Setup.cs index 54643c1..b689d28 100644 --- a/src/Mimic/Mimic`1.Setup.cs +++ b/src/Mimic/Mimic`1.Setup.cs @@ -7,18 +7,39 @@ namespace Mimic; public partial class Mimic { + /// + /// Sets up a method or property on the mocked object to be configured with behaviours. + /// + /// An expression that specifies the method or property to set up. + /// An that can be used to configure the behaviour of the specified method or property. + /// Thrown when is null. public ISetup Setup(Expression> expression) { var setup = Setup(this, expression); return new Setup(setup); } + /// + /// Sets up a method or property on the mocked object that returns a value to be configured with behaviours. + /// + /// The type of the return value of the method or property being set up. + /// An expression that specifies the method or property to set up. + /// An that can be used to configure the behaviour and return value of the specified method or property. + /// Thrown when is null. public ISetup Setup(Expression> expression) { var setup = Setup(this, expression); return new Setup(setup); } + /// + /// Sets up a property getter on the mocked object to be configured with behaviours. + /// + /// The type of the property being set up. + /// An expression that specifies the property getter to set up. + /// An that can be used to configure the behaviour of the specified property getter. + /// Thrown when is null. + /// Thrown when the expression does not represent a property or the property cannot be read. public IGetterSetup SetupGet(Expression> expression) { Guard.NotNull(expression); @@ -36,6 +57,13 @@ public IGetterSetup SetupGet(Expression(setup); } + /// + /// Sets up a property setter on the mocked object to be configured with behaviours. + /// + /// An action that specifies the property setter to set up. + /// An that can be used to configure the behaviour of the specified property setter. + /// Thrown when is null. + /// Thrown when the expression does not represent a valid property setter. public ISetup SetupSet(Action setterExpression) { Guard.NotNull(setterExpression); @@ -47,6 +75,14 @@ public ISetup SetupSet(Action setterExpression) return new Setup(setup); } + /// + /// Sets up a property setter on the mocked object to be configured with behaviours, with strongly typed property support. + /// + /// The type of the property being set up. + /// An action that specifies the property setter to set up. + /// An that can be used to configure the behaviour of the specified property setter. + /// Thrown when is null. + /// Thrown when the expression does not represent a valid property setter. public ISetterSetup SetupSet(Action setterExpression) { Guard.NotNull(setterExpression); @@ -58,6 +94,15 @@ public ISetterSetup SetupSet(Action setterExpression return new SetterSetup(setup); } + /// + /// Sets up a property on the mocked object to behave like a real property with automatic getter and setter behaviour. + /// + /// The type of the property being set up. + /// An expression that specifies the property to set up. + /// The initial value to assign to the property. Defaults to the default value of . + /// The current instance for method chaining. + /// Thrown when is null. + /// Thrown when the expression does not represent a property, or the property cannot be read or written. public Mimic SetupProperty(Expression> propertyExpression, TProperty? initialValue = default) { Guard.NotNull(propertyExpression); @@ -81,6 +126,10 @@ public Mimic SetupProperty(Expression> property return this; } + /// + /// Sets up all properties on the mocked object to behave like real properties with automatic getter and setter behaviour. + /// + /// The current instance for method chaining. public Mimic SetupAllProperties() { _setups.Add(new AllPropertiesStubSetup(this)); diff --git a/src/Mimic/Mimic`1.VerifyReceived.cs b/src/Mimic/Mimic`1.VerifyReceived.cs index f17b220..11d0d44 100644 --- a/src/Mimic/Mimic`1.VerifyReceived.cs +++ b/src/Mimic/Mimic`1.VerifyReceived.cs @@ -5,10 +5,20 @@ namespace Mimic; public partial class Mimic { + /// + /// Verifies that the mimic has received all expected setups. + /// public void VerifyExpectedReceived() => VerifyReceived(s => s.Expected, []); + /// + /// Verifies that the mimic has received all configured setups. + /// public void VerifyAllSetupsReceived() => VerifyReceived((SetupBase _) => true, []); + /// + /// Verifies that the mimic has received no other calls beyond those already verified. + /// + /// Thrown when unverified invocations are found. public void VerifyNoOtherCallsReceived() { lock (_invocations) @@ -21,15 +31,43 @@ public void VerifyNoOtherCallsReceived() #region VerifyReceived + /// + /// Verifies that the specified method call expression was received at least once. + /// + /// The expression representing the method call to verify. + /// Thrown when the expression is null. + /// Thrown when the expected invocation was not received. public void VerifyReceived(Expression> expression) => VerifyReceivedInternal(expression, CallCount.AtLeastOnce()); + /// + /// Verifies that the specified method call expression was received the expected number of times. + /// + /// The expression representing the method call to verify. + /// The expected number of calls. + /// Thrown when the expression is null. + /// Thrown when the expected invocation count was not met. public void VerifyReceived(Expression> expression, CallCount callCount) => VerifyReceivedInternal(expression, callCount); + /// + /// Verifies that the specified method call expression was received at least once, with a custom failure message. + /// + /// The expression representing the method call to verify. + /// The custom message to display when verification fails. + /// Thrown when the expression is null. + /// Thrown when the expected invocation was not received. public void VerifyReceived(Expression> expression, string failureMessage) => VerifyReceivedInternal(expression, CallCount.AtLeastOnce(), failureMessage); + /// + /// Verifies that the specified method call expression was received the expected number of times, with a custom failure message. + /// + /// The expression representing the method call to verify. + /// The expected number of calls. + /// The custom message to display when verification fails. + /// Thrown when the expression is null. + /// Thrown when the expected invocation count was not met. public void VerifyReceived(Expression> expression, CallCount callCount, string failureMessage) => VerifyReceivedInternal(expression, callCount, failureMessage); @@ -37,15 +75,47 @@ public void VerifyReceived(Expression> expression, CallCount callCount #region VerifyReceived + /// + /// Verifies that the specified method call expression returning a value was received at least once. + /// + /// The return type of the method being verified. + /// The expression representing the method call to verify. + /// Thrown when the expression is null. + /// Thrown when the expected invocation was not received. public void VerifyReceived(Expression> expression) => VerifyReceivedInternal(expression, CallCount.AtLeastOnce()); + /// + /// Verifies that the specified method call expression returning a value was received the expected number of times. + /// + /// The return type of the method being verified. + /// The expression representing the method call to verify. + /// The expected number of calls. + /// Thrown when the expression is null. + /// Thrown when the expected invocation count was not met. public void VerifyReceived(Expression> expression, CallCount callCount) => VerifyReceivedInternal(expression, callCount); + /// + /// Verifies that the specified method call expression returning a value was received at least once, with a custom failure message. + /// + /// The return type of the method being verified. + /// The expression representing the method call to verify. + /// The custom message to display when verification fails. + /// Thrown when the expression is null. + /// Thrown when the expected invocation was not received. public void VerifyReceived(Expression> expression, string failureMessage) => VerifyReceivedInternal(expression, CallCount.AtLeastOnce(), failureMessage); + /// + /// Verifies that the specified method call expression returning a value was received the expected number of times, with a custom failure message. + /// + /// The return type of the method being verified. + /// The expression representing the method call to verify. + /// The expected number of calls. + /// The custom message to display when verification fails. + /// Thrown when the expression is null. + /// Thrown when the expected invocation count was not met. public void VerifyReceived(Expression> expression, CallCount callCount, string failureMessage) => VerifyReceivedInternal(expression, callCount, failureMessage); @@ -53,15 +123,43 @@ public void VerifyReceived(Expression> expression, Cal #region VerifyGetReceived + /// + /// Verifies that the specified property getter was accessed at least once. + /// + /// The type of the property being verified. + /// The expression representing the property getter to verify. + /// Thrown when the expression is not a valid property getter or when the expected access was not received. public void VerifyGetReceived(Expression> expression) => VerifyGetReceivedInternal(expression, CallCount.AtLeastOnce()); + /// + /// Verifies that the specified property getter was accessed the expected number of times. + /// + /// The type of the property being verified. + /// The expression representing the property getter to verify. + /// The expected number of accesses. + /// Thrown when the expression is not a valid property getter or when the expected access count was not met. public void VerifyGetReceived(Expression> expression, CallCount callCount) => VerifyGetReceivedInternal(expression, callCount); + /// + /// Verifies that the specified property getter was accessed at least once, with a custom failure message. + /// + /// The type of the property being verified. + /// The expression representing the property getter to verify. + /// The custom message to display when verification fails. + /// Thrown when the expression is not a valid property getter or when the expected access was not received. public void VerifyGetReceived(Expression> expression, string failureMessage) => VerifyGetReceivedInternal(expression, CallCount.AtLeastOnce(), failureMessage); + /// + /// Verifies that the specified property getter was accessed the expected number of times, with a custom failure message. + /// + /// The type of the property being verified. + /// The expression representing the property getter to verify. + /// The expected number of accesses. + /// The custom message to display when verification fails. + /// Thrown when the expression is not a valid property getter or when the expected access count was not met. public void VerifyGetReceived(Expression> expression, CallCount callCount, string failureMessage) => VerifyGetReceivedInternal(expression, callCount, failureMessage); @@ -69,15 +167,43 @@ public void VerifyGetReceived(Expression> expressi #region VerifySetReceived + /// + /// Verifies that the specified property setter was called at least once. + /// + /// The expression representing the property setter to verify. + /// Thrown when setterExpression is null. + /// Thrown when the expression is not a valid property setter or when the expected call was not received. public void VerifySetReceived(Action setterExpression) => VerifySetReceivedInternal(setterExpression, CallCount.AtLeastOnce()); + /// + /// Verifies that the specified property setter was called the expected number of times. + /// + /// The expression representing the property setter to verify. + /// The expected number of calls. + /// Thrown when setterExpression is null. + /// Thrown when the expression is not a valid property setter or when the expected call count was not met. public void VerifySetReceived(Action setterExpression, CallCount callCount) => VerifySetReceivedInternal(setterExpression, callCount); + /// + /// Verifies that the specified property setter was called at least once, with a custom failure message. + /// + /// The expression representing the property setter to verify. + /// The custom message to display when verification fails. + /// Thrown when setterExpression is null. + /// Thrown when the expression is not a valid property setter or when the expected call was not received. public void VerifySetReceived(Action setterExpression, string failureMessage) => VerifySetReceivedInternal(setterExpression, CallCount.AtLeastOnce(), failureMessage); + /// + /// Verifies that the specified property setter was called the expected number of times, with a custom failure message. + /// + /// The expression representing the property setter to verify. + /// The expected number of calls. + /// The custom message to display when verification fails. + /// Thrown when setterExpression is null. + /// Thrown when the expression is not a valid property setter or when the expected call count was not met. public void VerifySetReceived(Action setterExpression, CallCount callCount, string failureMessage) => VerifySetReceivedInternal(setterExpression, callCount, failureMessage); diff --git a/src/Mimic/Mimic`1.cs b/src/Mimic/Mimic`1.cs index 646a104..7c008a1 100644 --- a/src/Mimic/Mimic`1.cs +++ b/src/Mimic/Mimic`1.cs @@ -17,10 +17,29 @@ public sealed partial class Mimic : IMimic private object[]? _constructorArguments; private T? _object; + /// + /// Gets the unique name identifier for this mimic instance. + /// + /// A string representing the mimic's name in the format "Mimic<TypeName>:InstanceNumber". public string Name { get; init; } + /// + /// Gets a value indicating whether this mimic operates in strict mode. + /// + /// true if the mimic is in strict mode; otherwise, false. The default value is true. + /// + /// In strict mode, the mimic will enforce that all method calls have corresponding setups. + /// public bool Strict { get; init; } = true; + /// + /// Gets or sets the constructor arguments to be used when creating the mimicked object. + /// + /// An array of objects to be passed as constructor arguments, or null if no arguments are needed. + /// Thrown when attempting to set constructor arguments for an interface type. + /// + /// Constructor arguments should only be provided when mimicking concrete classes, not interfaces. + /// public object[]? ConstructorArguments { get => _constructorArguments; @@ -33,6 +52,13 @@ public object[]? ConstructorArguments } } + /// + /// Gets the mimicked object instance. + /// + /// The proxy object that implements the behaviour defined by the mimic setups. + /// + /// The object is lazily initialised upon first access. Subsequent calls return the same instance. + /// public T Object => GetOrInitializeObject(); internal IReadOnlyList Invocations @@ -40,6 +66,13 @@ internal IReadOnlyList Invocations get { lock (_invocations) return _invocations.ToArray(); } } + /// + /// Initialises a new instance of the class with strict mode enabled. + /// + /// Thrown when the type cannot be mimicked. + /// + /// The mimic will operate in strict mode by default, requiring all method calls to have corresponding setups. + /// public Mimic() { if (!typeof(T).CanBeMimicked()) @@ -49,8 +82,27 @@ public Mimic() Name = $"Mimic<{TypeNameFormatter.GetFormattedName(typeof(T))}>:{instanceNumber}"; } + /// + /// Initialises a new instance of the class with the specified strict mode setting. + /// + /// true to enable strict mode; false to disable it. + /// Thrown when the type cannot be mimicked. + /// + /// In strict mode, all method calls must have corresponding setups. In non-strict mode, + /// method calls without setups will return default values or perform no action. + /// public Mimic(bool strict) : this() => Strict = strict; + /// + /// Retrieves the mimic instance from an object that was created by a mimic. + /// + /// The object instance that was created by a mimic. + /// The instance that created the specified object. + /// Thrown when the provided object was not created by a mimic. + /// + /// This method allows you to get the original mimic instance from a mimicked object, + /// which is useful for verification or additional setup operations. + /// public static Mimic FromObject(T objectInstance) { if (objectInstance is not IMimicked mimicked) @@ -59,8 +111,21 @@ public static Mimic FromObject(T objectInstance) return mimicked.Mimic; } + /// + /// Creates a conditional setup that applies only when the specified condition is met. + /// + /// A function that returns true when the conditional setup should be active. + /// An that can be used to define behaviour for the conditional setup. + /// + /// Conditional setups allow you to define different behaviours based on runtime conditions. + /// The condition is evaluated each time a matching method is called. + /// public IConditionalSetup When(Func condition) => new ConditionalSetup(this, condition); + /// + /// Returns a string representation of this mimic instance. + /// + /// The name of this mimic instance. public override string ToString() => Name; private T GetOrInitializeObject()