diff --git a/Docs/pages/02-setup.md b/Docs/pages/02-setup.md deleted file mode 100644 index 0fd64d46..00000000 --- a/Docs/pages/02-setup.md +++ /dev/null @@ -1,163 +0,0 @@ -# Setup - -Set up return values or behaviors for methods, properties, and indexers on your mock. Control how the mock responds to -calls in your tests. - -## Method Setup - -Use `mock.SetupMock.Method.MethodName(…)` to set up methods. You can specify argument matchers for each parameter. - -```csharp -// Setup Dispense to decrease stock and raise event -sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny()) - .Returns((type, amount) => - { - var current = sut[type]; - if (current >= amount) - { - sut[type] = current - amount; - sut.RaiseOnMock.ChocolateDispensed(type, amount); - return true; - } - return false; - }); - -// Setup method with callback -sut.SetupMock.Method.Dispense(It.Is("White"), It.IsAny()) - .Do((type, amount) => Console.WriteLine($"Dispensed {amount} {type} chocolate.")); - -// Setup method to throw -sut.SetupMock.Method.Dispense(It.Is("Green"), It.IsAny()) - .Throws(); -``` - -- Use `.Do(…)` to run code when the method is called. Supports parameterless or parameter callbacks. -- Use `.Returns(…)` to specify the value to return. You can provide a direct value, a callback, or a callback with - parameters. -- Use `.Throws(…)` to specify an exception to throw. Supports direct exceptions, exception factories, or factories with - parameters. -- Use `.Returns(…)` and `.Throws(…)` repeatedly to define a sequence of return values or exceptions (cycled on each - call). -- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific method (only for class mocks). -- When you specify overlapping setups, the most recently defined setup takes precedence. - -**Async Methods** - -For `Task` or `ValueTask` methods, use `.ReturnsAsync(…)`: - -```csharp -sut.SetupMock.Method.DispenseAsync(It.IsAny(), It.IsAny()) - .ReturnsAsync(true); -``` - -#### Parameter Matching - -Mockolate provides flexible parameter matching for method setups and verifications: - -- `It.IsAny()`: Matches any value of type `T`. -- `It.Is(value)`: Matches a specific value. With `.Using(IEqualityComparer)`, you can provide a custom equality - comparer. -- `It.IsOneOf(params T[] values)`: Matches any of the given values. With `.Using(IEqualityComparer)`, you can - provide a custom equality comparer. -- `It.IsNull()`: Matches null. -- `It.IsTrue()`/`It.IsFalse()`: Matches boolean true/false. -- `It.IsInRange(min, max)`: Matches a number within the given range. You can append `.Exclusive()` to exclude the - minimum and maximum value. -- `It.IsOut(…)`/`It.IsAnyOut(…)`: Matches and sets out parameters, supports value setting and - predicates. -- `It.IsRef(…)`/`It.IsAnyRef(…)`: Matches and sets ref parameters, supports value setting and - predicates. -- `It.Matches(pattern)`: Matches strings using wildcard patterns (`*` and `?`). With `.AsRegex()`, you can use - regular expressions instead. -- `It.Satisfies(predicate)`: Matches values based on a predicate. - -When the method name is unique (no overloads), you can omit the argument matchers for simpler setups: - -- `Match.AnyParameters()`: Matches any parameters. -- `Match.Parameters(Func predicate)`: Matches the parameters based on a predicate. - -#### Parameter Interaction - -With `Do`, you can register a callback for individual parameters of a method setup. This allows you to implement side -effects or checks directly when the method or indexer is called. With `.Monitor(out monitor)`, you can track the actual -values passed during test execution and analyze them afterwards. - -**Example: Do for method parameter** - -```csharp -int lastAmount = 0; -sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Do(amount => lastAmount = amount)); -sut.Dispense("Dark", 42); -// lastAmount == 42 -``` - -**Example: Monitor for method parameter** - -```csharp -Mockolate.ParameterMonitor monitor; -sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Monitor(out monitor)); -sut.Dispense("Dark", 5); -sut.Dispense("Dark", 7); -// monitor.Values == [5, 7] -``` - -## Property Setup - -Set up property getters and setters to control or verify property access on your mocks. - -**Initialization** - -You can initialize properties so they work like normal properties (setter changes the value, getter returns the last set -value): - -```csharp -sut.SetupMock.Property.TotalDispensed.InitializeWith(42); -``` - -**Returns / Throws** - -Alternatively, set up properties with `Returns` and `Throws` (supports sequences): - -```csharp -sut.SetupMock.Property.TotalDispensed - .Returns(1) - .Returns(2) - .Throws(new Exception("Error")) - .Returns(4); -``` - -**Callbacks** - -Register callbacks on the setter or getter: - -```csharp -sut.SetupMock.Property.TotalDispensed.OnGet(() => Console.WriteLine("TotalDispensed was read!")); -sut.SetupMock.Property.TotalDispensed.OnSet((oldValue, newValue) => Console.WriteLine($"Changed from {oldValue} to {newValue}!") ); -``` - -## Indexer Setup - -Set up indexers with argument matchers. Supports initialization, returns/throws sequences, and callbacks. - -```csharp -sut.SetupMock.Indexer(It.IsAny()) - .InitializeWith(type => 20) - .OnGet(type => Console.WriteLine($"Stock for {type} was read")); - -sut.SetupMock.Indexer(It.Is("Dark")) - .InitializeWith(10) - .OnSet((value, type) => Console.WriteLine($"Set [{type}] to {value}")); -``` - -- `.InitializeWith(…)` can take a value or a callback with parameters. -- `.Returns(…)` and `.Throws(…)` support direct values, callbacks, and callbacks with parameters and/or the current - value. -- `.OnGet(…)` and `.OnSet(…)` support callbacks with or without parameters. -- `.Returns(…)` and `.Throws(…)` can be chained to define a sequence of behaviors, which are cycled through on each - call. -- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific indexer (only for class mocks). -- When you specify overlapping setups, the most recently defined setup takes precedence. - -**Note**: -You can use the same [parameter matching](#parameter-matching) and [interaction](#parameter-interaction) options as for -methods. diff --git a/Docs/pages/setup/01-properties.md b/Docs/pages/setup/01-properties.md new file mode 100644 index 00000000..e287a0d7 --- /dev/null +++ b/Docs/pages/setup/01-properties.md @@ -0,0 +1,33 @@ +# Properties + +Set up property getters and setters to control or verify property access on your mocks. + +**Initialization** + +You can initialize properties so they work like normal properties (setter changes the value, getter returns the last set +value): + +```csharp +sut.SetupMock.Property.TotalDispensed.InitializeWith(42); +``` + +**Returns / Throws** + +Alternatively, set up properties with `Returns` and `Throws` (supports sequences): + +```csharp +sut.SetupMock.Property.TotalDispensed + .Returns(1) + .Returns(2) + .Throws(new Exception("Error")) + .Returns(4); +``` + +**Callbacks** + +Register callbacks on the setter or getter: + +```csharp +sut.SetupMock.Property.TotalDispensed.OnGet.Do(() => Console.WriteLine("TotalDispensed was read!")); +sut.SetupMock.Property.TotalDispensed.OnSet.Do((oldValue, newValue) => Console.WriteLine($"Changed from {oldValue} to {newValue}!") ); +``` diff --git a/Docs/pages/setup/02-methods.md b/Docs/pages/setup/02-methods.md new file mode 100644 index 00000000..e023e7a7 --- /dev/null +++ b/Docs/pages/setup/02-methods.md @@ -0,0 +1,46 @@ +# Methods + +Use `mock.SetupMock.Method.MethodName(…)` to set up methods. You can specify argument matchers for each parameter. + +```csharp +// Setup Dispense to decrease stock and raise event +sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny()) + .Returns((type, amount) => + { + var current = sut[type]; + if (current >= amount) + { + sut[type] = current - amount; + sut.RaiseOnMock.ChocolateDispensed(type, amount); + return true; + } + return false; + }); + +// Setup method with callback +sut.SetupMock.Method.Dispense(It.Is("White"), It.IsAny()) + .Do((type, amount) => Console.WriteLine($"Dispensed {amount} {type} chocolate.")); + +// Setup method to throw +sut.SetupMock.Method.Dispense(It.Is("Green"), It.IsAny()) + .Throws(); +``` + +- Use `.Do(…)` to run code when the method is called. Supports parameterless or parameter callbacks. +- Use `.Returns(…)` to specify the value to return. You can provide a direct value, a callback, or a callback with + parameters. +- Use `.Throws(…)` to specify an exception to throw. Supports direct exceptions, exception factories, or factories with + parameters. +- Use `.Returns(…)` and `.Throws(…)` repeatedly to define a sequence of return values or exceptions (cycled on each + call). +- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific method (only for class mocks). +- When you specify overlapping setups, the most recently defined setup takes precedence. + +**Async Methods** + +For `Task` or `ValueTask` methods, use `.ReturnsAsync(…)`: + +```csharp +sut.SetupMock.Method.DispenseAsync(It.IsAny(), It.IsAny()) + .ReturnsAsync(true); +``` diff --git a/Docs/pages/setup/03-indexers.md b/Docs/pages/setup/03-indexers.md new file mode 100644 index 00000000..8fe95e5a --- /dev/null +++ b/Docs/pages/setup/03-indexers.md @@ -0,0 +1,27 @@ +# Indexers + +Set up indexers with argument matchers. Supports initialization, returns/throws sequences, and callbacks. + +```csharp +sut.SetupMock.Indexer(It.IsAny()) + .InitializeWith(type => 20) + .OnGet.Do(type => Console.WriteLine($"Stock for {type} was read")); + +sut.SetupMock.Indexer(It.Is("Dark")) + .InitializeWith(10) + .OnSet.Do((value, type) => Console.WriteLine($"Set [{type}] to {value}")); +``` + +- `.InitializeWith(…)` can take a value or a callback with parameters. +- `.Returns(…)` and `.Throws(…)` support direct values, callbacks, and callbacks with parameters and/or the current + value. +- `.OnGet.Do(…)` and `.OnSet.Do(…)` support callbacks with or without parameters. +- `.Returns(…)` and `.Throws(…)` can be chained to define a sequence of behaviors, which are cycled through on each + call. +- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific indexer (only for class mocks). +- When you specify overlapping setups, the most recently defined setup takes precedence. + +**Note**: +You can use the same [parameter matching](https://awexpect.com/docs/mockolate/setup/parameter-matching) +and [interaction](https://awexpect.com/docs/mockolate/setup/parameter-matching#parameter-interaction) options as for +methods. diff --git a/Docs/pages/setup/04-parameter-matching.md b/Docs/pages/setup/04-parameter-matching.md new file mode 100644 index 00000000..4fd10963 --- /dev/null +++ b/Docs/pages/setup/04-parameter-matching.md @@ -0,0 +1,148 @@ +# Parameter Matching + +Mockolate provides flexible parameter matching for method setups and verifications: + +## Parameter Matchers + +### Basic Matchers + +- `It.IsAny()`: Matches any value of type `T`. +- `It.Is(value)`: Matches a specific value. +- `It.IsOneOf(params T[] values)`: Matches any of the given values. +- `It.IsNull()`: Matches null. +- `It.IsTrue()`/`It.IsFalse()`: Matches boolean true/false. +- `It.IsInRange(min, max)`: Matches a number within the given range. You can append `.Exclusive()` to exclude the + minimum and maximum value. +- `It.Satisfies(predicate)`: Matches values based on a predicate. + +### String Matching + +- `It.Matches(pattern)`: Matches strings using wildcard patterns (`*` and `?`). + + **Regular Expressions** + Use `.AsRegex()` to enable regular expression matching for `It.Matches()`: + + ```csharp + // Example: Match email addresses + sut.SetupMock.Method.ValidateEmail(It.Matches(@"^\w+@\w+\.\w+$").AsRegex()) + .Returns(true); + + bool result = sut.ValidateEmail("user@example.com"); + + // Case-sensitive regex + sut.SetupMock.Method.Process(It.Matches("^[A-Z]+$").AsRegex().CaseSensitive()) + .Returns(1); + ``` + +### Ref and Out Parameters + +- `It.IsRef(setter)`: Matches any `ref` parameter and sets a new value using the setter function. +- `It.IsRef(predicate, setter)`: Matches `ref` parameters that satisfy the predicate and sets a new value. +- `It.IsRef(predicate)`: Matches `ref` parameters that satisfy the predicate without changing the value. +- `It.IsAnyRef()`: Matches any `ref` parameter without restrictions. +- `It.IsOut(setter)`: Matches any `out` parameter and sets a value using the setter function. +- `It.IsAnyOut()`: Matches any `out` parameter without restrictions and sets the parameter to the default value of + `T`. + +```csharp +// Example: Setup with out parameter +sut.SetupMock.Method.TryParse(It.IsAny(), It.IsOut(() => 42)) + .Returns(true); + +int result; +bool success = sut.TryParse("abc", out result); +// result == 42, success == true + +// Example: Setup with ref parameter +sut.SetupMock.Method.Increment(It.IsRef(v => v + 1)) + .Returns(true); + +int value = 5; +sut.Increment(ref value); +// value == 6 +``` + +### Span Parameters (.NET 8+) + +- `It.IsSpan(predicate)`: Matches `Span` parameters that satisfy the predicate. +- `It.IsAnySpan()`: Matches any `Span` parameter. +- `It.IsReadOnlySpan(predicate)`: Matches `ReadOnlySpan` parameters that satisfy the predicate. +- `It.IsAnyReadOnlySpan()`: Matches any `ReadOnlySpan` parameter. + +**Note:** +As `ref struct` types cannot be stored directly, it is converted to an array internally and the `predicate` receives +this array for evaluation. + +```csharp +// Example: Setup with Span parameter +sut.SetupMock.Method.Process(It.IsSpan(data => data.Length > 0)) + .Returns(true); + +Span buffer = new byte[] { 1, 2, 3 }; +bool result = sut.Process(buffer); +// result == true +``` + +### Custom Equality Comparers + +Use `.Using(IEqualityComparer)` to provide custom equality comparison for `It.Is()` and `It.IsOneOf()`: + +```csharp +// Example: Case-insensitive string comparison +var comparer = StringComparer.OrdinalIgnoreCase; +sut.SetupMock.Method.Process(It.Is("hello").Using(comparer)) + .Returns(42); + +int result = sut.Process("HELLO"); +// result == 42 +``` + +## Parameter Predicates + +When the method name is unique (no overloads), you can use flexible parameter matching: + +- `Match.AnyParameters()`: Matches any parameter combination. +- `Match.Parameters(Func predicate)`: Matches parameters based on a custom predicate. + +```csharp +// Example: Custom parameter predicate +sut.SetupMock.Method.Process(Match.Parameters(args => + args.Length == 2 && + args[0].Value is string s && s.StartsWith("test") && + args[1].Value is int i && i > 0)) + .Returns(true); + +bool result = sut.Process("test123", 5); +// result == true +``` + +## Parameter Interaction + +### Callbacks + +With `.Do`, you can register a callback for individual parameters of a method setup. This allows you to implement side +effects or checks directly when the method or indexer is called. + +**Example: Do for method parameter** + +```csharp +int lastAmount = 0; +sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Do(amount => lastAmount = amount)); +sut.Dispense("Dark", 42); +// lastAmount == 42 +``` + +### Monitor + +With `.Monitor(out monitor)`, you can track the actual +values passed during test execution and analyze them afterward. + +**Example: Monitor for method parameter** + +```csharp +Mockolate.ParameterMonitor monitor; +sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Monitor(out monitor)); +sut.Dispense("Dark", 5); +sut.Dispense("Dark", 7); +// monitor.Values == [5, 7] +``` diff --git a/Docs/pages/setup/_category_.json b/Docs/pages/setup/_category_.json new file mode 100644 index 00000000..23ac8af8 --- /dev/null +++ b/Docs/pages/setup/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Setup", + "collapsed": false, + "position": 2, + "link": { + "type": "generated-index" + }, + "customProps": { + "description": "Set up return values or behaviors for methods, properties, and indexers on your mock. Control how the mock responds to calls in your tests." + } +} diff --git a/Mockolate.slnx b/Mockolate.slnx index aa75d4d5..abd6ea6e 100644 --- a/Mockolate.slnx +++ b/Mockolate.slnx @@ -43,7 +43,6 @@ - @@ -52,6 +51,13 @@ + + + + + + + diff --git a/README.md b/README.md index 5014644e..a72960c2 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ var sut2 = factory.Create(); Using a factory allows you to create multiple mocks with identical, centrally configured behavior. This is especially useful when you need consistent mock setups across multiple tests or for different types. -## Wrapping existing instances +### Wrapping existing instances You can wrap an existing instance with mock tracking using `Mock.Wrap()`. This allows you to track interactions with a real object: @@ -178,7 +178,41 @@ wrappedDispenser.VerifyMock.Invoked.Dispense(It.Is("Dark"), It.Is(5)).Once(); Set up return values or behaviors for methods, properties, and indexers on your mock. Control how the mock responds to calls in your tests. -### Method Setup +### Properties + +Set up property getters and setters to control or verify property access on your mocks. + +**Initialization** + +You can initialize properties so they work like normal properties (setter changes the value, getter returns the last set +value): + +```csharp +sut.SetupMock.Property.TotalDispensed.InitializeWith(42); +``` + +**Returns / Throws** + +Alternatively, set up properties with `Returns` and `Throws` (supports sequences): + +```csharp +sut.SetupMock.Property.TotalDispensed + .Returns(1) + .Returns(2) + .Throws(new Exception("Error")) + .Returns(4); +``` + +**Callbacks** + +Register callbacks on the setter or getter: + +```csharp +sut.SetupMock.Property.TotalDispensed.OnGet.Do(() => Console.WriteLine("TotalDispensed was read!")); +sut.SetupMock.Property.TotalDispensed.OnSet.Do((oldValue, newValue) => Console.WriteLine($"Changed from {oldValue} to {newValue}!") ); +``` + +### Methods Use `mock.SetupMock.Method.MethodName(…)` to set up methods. You can specify argument matchers for each parameter. @@ -225,119 +259,181 @@ sut.SetupMock.Method.DispenseAsync(It.IsAny(), It.IsAny()) .ReturnsAsync(true); ``` -#### Parameter Matching +### Indexers + +Set up indexers with argument matchers. Supports initialization, returns/throws sequences, and callbacks. + +```csharp +sut.SetupMock.Indexer(It.IsAny()) + .InitializeWith(type => 20) + .OnGet.Do(type => Console.WriteLine($"Stock for {type} was read")); + +sut.SetupMock.Indexer(It.Is("Dark")) + .InitializeWith(10) + .OnSet.Do((value, type) => Console.WriteLine($"Set [{type}] to {value}")); +``` + +- `.InitializeWith(…)` can take a value or a callback with parameters. +- `.Returns(…)` and `.Throws(…)` support direct values, callbacks, and callbacks with parameters and/or the current + value. +- `.OnGet.Do(…)` and `.OnSet.Do(…)` support callbacks with or without parameters. +- `.Returns(…)` and `.Throws(…)` can be chained to define a sequence of behaviors, which are cycled through on each + call. +- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific indexer (only for class mocks). +- When you specify overlapping setups, the most recently defined setup takes precedence. + +**Note**: +You can use the same [parameter matching](#parameter-matching) and [interaction](#parameter-interaction) options as for +methods. + +### Parameter Matching Mockolate provides flexible parameter matching for method setups and verifications: +#### Parameter Matchers + +##### Basic Matchers + - `It.IsAny()`: Matches any value of type `T`. -- `It.Is(value)`: Matches a specific value. With `.Using(IEqualityComparer)`, you can provide a custom equality - comparer. -- `It.IsOneOf(params T[] values)`: Matches any of the given values. With `.Using(IEqualityComparer)`, you can - provide a custom equality comparer. +- `It.Is(value)`: Matches a specific value. +- `It.IsOneOf(params T[] values)`: Matches any of the given values. - `It.IsNull()`: Matches null. - `It.IsTrue()`/`It.IsFalse()`: Matches boolean true/false. - `It.IsInRange(min, max)`: Matches a number within the given range. You can append `.Exclusive()` to exclude the minimum and maximum value. -- `It.IsOut(…)`/`It.IsAnyOut(…)`: Matches and sets out parameters, supports value setting and - predicates. -- `It.IsRef(…)`/`It.IsAnyRef(…)`: Matches and sets ref parameters, supports value setting and - predicates. -- `It.Matches(pattern)`: Matches strings using wildcard patterns (`*` and `?`). With `.AsRegex()`, you can use - regular expressions instead. - `It.Satisfies(predicate)`: Matches values based on a predicate. -When the method name is unique (no overloads), you can omit the argument matchers for simpler setups: +##### String Matching -- `Match.AnyParameters()`: Matches any parameters. -- `Match.Parameters(Func predicate)`: Matches the parameters based on a predicate. +- `It.Matches(pattern)`: Matches strings using wildcard patterns (`*` and `?`). -```csharp + **Regular Expressions** + Use `.AsRegex()` to enable regular expression matching for `It.Matches()`: -#### Parameter Interaction + ```csharp + // Example: Match email addresses + sut.SetupMock.Method.ValidateEmail(It.Matches(@"^\w+@\w+\.\w+$").AsRegex()) + .Returns(true); + + bool result = sut.ValidateEmail("user@example.com"); + + // Case-sensitive regex + sut.SetupMock.Method.Process(It.Matches("^[A-Z]+$").AsRegex().CaseSensitive()) + .Returns(1); + ``` -With `Do`, you can register a callback for individual parameters of a method setup. This allows you to implement side -effects or checks directly when the method or indexer is called. With `.Monitor(out monitor)`, you can track the actual -values passed during test execution and analyze them afterwards. +##### Ref and Out Parameters -**Example: Do for method parameter** +- `It.IsRef(setter)`: Matches any `ref` parameter and sets a new value using the setter function. +- `It.IsRef(predicate, setter)`: Matches `ref` parameters that satisfy the predicate and sets a new value. +- `It.IsRef(predicate)`: Matches `ref` parameters that satisfy the predicate without changing the value. +- `It.IsAnyRef()`: Matches any `ref` parameter without restrictions. +- `It.IsOut(setter)`: Matches any `out` parameter and sets a value using the setter function. +- `It.IsAnyOut()`: Matches any `out` parameter without restrictions and sets the parameter to the default value of + `T`. ```csharp -int lastAmount = 0; -sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Do(amount => lastAmount = amount)); -sut.Dispense("Dark", 42); -// lastAmount == 42 -``` +// Example: Setup with out parameter +sut.SetupMock.Method.TryParse(It.IsAny(), It.IsOut(() => 42)) + .Returns(true); -**Example: Monitor for method parameter** +int result; +bool success = sut.TryParse("abc", out result); +// result == 42, success == true -```csharp -Mockolate.ParameterMonitor monitor; -sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Monitor(out monitor)); -sut.Dispense("Dark", 5); -sut.Dispense("Dark", 7); -// monitor.Values == [5, 7] -``` +// Example: Setup with ref parameter +sut.SetupMock.Method.Increment(It.IsRef(v => v + 1)) + .Returns(true); -### Property Setup +int value = 5; +sut.Increment(ref value); +// value == 6 +``` -Set up property getters and setters to control or verify property access on your mocks. +##### Span Parameters (.NET 8+) -**Initialization** +- `It.IsSpan(predicate)`: Matches `Span` parameters that satisfy the predicate. +- `It.IsAnySpan()`: Matches any `Span` parameter. +- `It.IsReadOnlySpan(predicate)`: Matches `ReadOnlySpan` parameters that satisfy the predicate. +- `It.IsAnyReadOnlySpan()`: Matches any `ReadOnlySpan` parameter. -You can initialize properties so they work like normal properties (setter changes the value, getter returns the last set -value): +**Note:** +As `ref struct` types cannot be stored directly, it is converted to an array internally and the `predicate` receives +this array for evaluation. ```csharp -sut.SetupMock.Property.TotalDispensed.InitializeWith(42); +// Example: Setup with Span parameter +sut.SetupMock.Method.Process(It.IsSpan(data => data.Length > 0)) + .Returns(true); + +Span buffer = new byte[] { 1, 2, 3 }; +bool result = sut.Process(buffer); +// result == true ``` -**Returns / Throws** +##### Custom Equality Comparers -Alternatively, set up properties with `Returns` and `Throws` (supports sequences): +Use `.Using(IEqualityComparer)` to provide custom equality comparison for `It.Is()` and `It.IsOneOf()`: ```csharp -sut.SetupMock.Property.TotalDispensed - .Returns(1) - .Returns(2) - .Throws(new Exception("Error")) - .Returns(4); +// Example: Case-insensitive string comparison +var comparer = StringComparer.OrdinalIgnoreCase; +sut.SetupMock.Method.Process(It.Is("hello").Using(comparer)) + .Returns(42); + +int result = sut.Process("HELLO"); +// result == 42 ``` -**Callbacks** +#### Parameter Predicates -Register callbacks on the setter or getter: +When the method name is unique (no overloads), you can use flexible parameter matching: + +- `Match.AnyParameters()`: Matches any parameter combination. +- `Match.Parameters(Func predicate)`: Matches parameters based on a custom predicate. ```csharp -sut.SetupMock.Property.TotalDispensed.OnGet(() => Console.WriteLine("TotalDispensed was read!")); -sut.SetupMock.Property.TotalDispensed.OnSet((oldValue, newValue) => Console.WriteLine($"Changed from {oldValue} to {newValue}!") ); +// Example: Custom parameter predicate +sut.SetupMock.Method.Process(Match.Parameters(args => + args.Length == 2 && + args[0].Value is string s && s.StartsWith("test") && + args[1].Value is int i && i > 0)) + .Returns(true); + +bool result = sut.Process("test123", 5); +// result == true ``` -### Indexer Setup +#### Parameter Interaction -Set up indexers with argument matchers. Supports initialization, returns/throws sequences, and callbacks. +##### Callbacks -```csharp -sut.SetupMock.Indexer(It.IsAny()) - .InitializeWith(type => 20) - .OnGet(type => Console.WriteLine($"Stock for {type} was read")); +With `.Do`, you can register a callback for individual parameters of a method setup. This allows you to implement side +effects or checks directly when the method or indexer is called. -sut.SetupMock.Indexer(It.Is("Dark")) - .InitializeWith(10) - .OnSet((value, type) => Console.WriteLine($"Set [{type}] to {value}")); +**Example: Do for method parameter** + +```csharp +int lastAmount = 0; +sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Do(amount => lastAmount = amount)); +sut.Dispense("Dark", 42); +// lastAmount == 42 ``` -- `.InitializeWith(…)` can take a value or a callback with parameters. -- `.Returns(…)` and `.Throws(…)` support direct values, callbacks, and callbacks with parameters and/or the current - value. -- `.OnGet(…)` and `.OnSet(…)` support callbacks with or without parameters. -- `.Returns(…)` and `.Throws(…)` can be chained to define a sequence of behaviors, which are cycled through on each - call. -- Use `.SkippingBaseClass(…)` to override the base class behavior for a specific indexer (only for class mocks). -- When you specify overlapping setups, the most recently defined setup takes precedence. +##### Monitor -**Note**: -You can use the same [parameter matching](#parameter-matching) and [interaction](#parameter-interaction) options as for -methods. +With `.Monitor(out monitor)`, you can track the actual +values passed during test execution and analyze them afterward. + +**Example: Monitor for method parameter** + +```csharp +Mockolate.ParameterMonitor monitor; +sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny().Monitor(out monitor)); +sut.Dispense("Dark", 5); +sut.Dispense("Dark", 7); +// monitor.Values == [5, 7] +``` ## Mock events