Parameter Matching
Mockolate provides flexible parameter matching for method setups and verifications:
Parameter Matchers
Basic Matchers
It.IsAny<T>(): Matches any value of typeT.It.Is<T>(value): Matches a specific value.It.IsOneOf<T>(params T[] values): Matches any of the given values.It.IsNull<T>(): 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<T>(predicate): Matches values based on a predicate.
Note:
You can also directly use a value (equivalent to wrapping it in It.Is<T>(value)). For methods and indexers with
up to 4 parameters, you can freely mix matchers with direct values in any position. For members with more than
4 parameters, Mockolate still supports explicit values, but limits arbitrary per-parameter mixing to avoid a
combinatorial explosion of overloads. You may need to use direct values for all explicit-value-capable parameters
and wrap the remaining ones, or use matchers for all parameters.
String Matching
It.Matches(pattern): Matches strings using wildcard patterns (*and?).
Regular Expressions
Use .AsRegex() to enable regular expression matching for It.Matches():
// Example: Match email addresses
sut.Mock.Setup.ValidateEmail(It.Matches(@"^\w+@\w+\.\w+$").AsRegex())
.Returns(true);
bool result = sut.ValidateEmail("user@example.com");
// Case-sensitive regex
sut.Mock.Setup.Process(It.Matches("^[A-Z]+$").AsRegex().CaseSensitive())
.Returns(1);
Ref and Out Parameters
It.IsRef<T>(setter): Matches anyrefparameter and sets a new value using the setter function.It.IsRef<T>(predicate, setter): Matchesrefparameters that satisfy the predicate and sets a new value.It.IsRef<T>(predicate): Matchesrefparameters that satisfy the predicate without changing the value.It.IsAnyRef<T>(): Matches anyrefparameter without restrictions.It.IsOut<T>(setter): Matches anyoutparameter and sets a value using the setter function.It.IsAnyOut<T>(): Matches anyoutparameter without restrictions and sets the parameter to the default value ofT.
// Example: Setup with out parameter
sut.Mock.Setup.TryParse(It.IsAny<string>(), It.IsOut(() => 42))
.Returns(true);
int result;
bool success = sut.TryParse("abc", out result);
// result == 42, success == true
// Example: Setup with ref parameter
sut.Mock.Setup.Increment(It.IsRef<int>(v => v + 1))
.Returns(true);
int value = 5;
sut.Increment(ref value);
// value == 6
Collection Matchers
It.Contains<T>(item): Matches a collection parameter that containsitem.It.SequenceEquals<T>(params IEnumerable<T> values): Matches a collection parameter whose elements equalvaluesin the same order.
Both matchers support method parameters declared as IEnumerable<T>, ICollection<T>, IList<T>,
IReadOnlyCollection<T>, IReadOnlyList<T>, T[], List<T>, Queue<T> or Stack<T>.
It.Contains<T> additionally supports the unordered shapes ISet<T> and HashSet<T>; It.SequenceEquals<T> intentionally
does not, because their enumeration order is not guaranteed.
Append .Using(IEqualityComparer<T>) to either matcher to control element equality.
// Example: Match a list that contains a specific item
sut.Mock.Setup.Process(It.Contains(5))
.Returns(true);
bool result = sut.Process(new[] { 1, 2, 5 });
// result == true
// Example: Match a sequence of items in order
sut.Mock.Setup.Process(It.SequenceEquals("a", "b", "c"))
.Returns(true);
bool match = sut.Process(new[] { "a", "b", "c" });
// match == true
// Example: Case-insensitive containment
sut.Mock.Setup.Process(It.Contains("HELLO").Using(StringComparer.OrdinalIgnoreCase))
.Returns(true);
Custom Equality Comparers
Use .Using(IEqualityComparer<T>) to provide custom equality comparison for It.Is() and It.IsOneOf():
// Example: Case-insensitive string comparison
var comparer = StringComparer.OrdinalIgnoreCase;
sut.Mock.Setup.Process(It.Is("hello").Using(comparer))
.Returns(42);
int result = sut.Process("HELLO");
// result == 42
Span Parameters (.NET 8+)
It.IsSpan<T>(predicate): MatchesSpan<T>parameters that satisfy the predicate.It.IsAnySpan<T>(): Matches anySpan<T>parameter.It.IsReadOnlySpan<T>(predicate): MatchesReadOnlySpan<T>parameters that satisfy the predicate.It.IsAnyReadOnlySpan<T>(): Matches anyReadOnlySpan<T>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.
// Example: Setup with Span parameter
sut.Mock.Setup.Process(It.IsSpan<byte>(data => data.Length > 0))
.Returns(true);
Span<byte> buffer = new byte[] { 1, 2, 3 };
bool result = sut.Process(buffer);
// result == true
Ref Struct Parameters (.NET 9+)
You can mock methods and indexers that take custom ref struct parameters (e.g. Utf8JsonReader
or your own ref struct Packet) using these matchers:
It.IsAnyRefStruct<T>(): Matches any ref-struct value of typeT.It.IsRefStruct<T>(predicate): Matches ref-struct values that satisfy the predicate. The predicate can read the struct's fields at the time the call is made.It.IsRefStructBy<T, TKey>(projection)/It.IsRefStructBy<T, TKey>(projection, predicate): For ref-struct-keyed indexers, projects the key to an equatable value so writes and reads can be correlated. Works at any arity — apply it to every ref-struct slot and non-ref-struct slots contribute their raw value to the composite dispatch key (see Indexer storage in the remarks).
public readonly ref struct Packet(int id, ReadOnlySpan<byte> payload)
{
public int Id { get; } = id;
public ReadOnlySpan<byte> Payload { get; } = payload;
}
public interface IPacketSink
{
void Consume(Packet packet);
int TryParse(Packet packet);
string this[Packet key] { get; set; }
}
// Match on the live ref struct, including its Span contents.
sut.Mock.Setup.Consume(It.IsRefStruct<Packet>(p =>
p.Payload.Length > 0 && p.Payload[0] == 0xFF))
.Throws<InvalidOperationException>();
// Return a value from a ref-struct-parameter method.
sut.Mock.Setup.TryParse(It.IsAnyRefStruct<Packet>()).Returns(42);
// Ref-struct-keyed indexer: Returns for get, OnSet for observed writes.
sut.Mock.Setup[It.IsAnyRefStruct<Packet>()]
.Returns("got")
.OnSet(value => { /* observed write */ });
// Correlate writes and reads by projecting the key to an equatable value.
sut.Mock.Setup[It.IsRefStructBy<Packet, int>(p => p.Id)].Returns("fallback");
sut[new Packet(1, [])] = "written";
string a = sut[new Packet(1, [])]; // "written" matched by Id
string b = sut[new Packet(2, [])]; // "fallback" no write under Id=2
Remarks
The ref-struct pipeline uses a narrower API than the rest of Mockolate. A handful of fluent
features are unavailable because the C# language does not let ref struct values flow through
generic delegates:
- Setup surface. Only
Returns(value),Returns(factory),Throws*,OnSet(Action<TValue>)(indexer setters), andSkippingBaseClassare available. The.Do(...)callback and theCallbacks<T>builder (InParallel,When,For,Only,TransitionTo) are not offered for ref-struct parameters. - Verify.
Verifycounts calls to the method but cannot match on the parameter value after the fact — the ref-struct value isn't retained past the call. Use a setup-time matcher to filter at call time. - Indexer storage. By default, values written through a ref-struct-keyed indexer setter are
not read back by the getter. Apply
It.IsRefStructBy<T, TKey>(projection)to every ref-struct slot to enable write-then-read correlation keyed by the projections; non-ref-struct slots contribute their raw value as part of the composite dispatch key. If any ref-struct slot is matched without a projection, storage stays inactive for that setup.
The following cases are rejected at compile time with diagnostic Mockolate0004:
- Targeting older than .NET 9 (the feature relies on
allows ref struct, a .NET 9 / C# 13 feature). out/ref/ref readonlyparameters of a ref-struct type.- Methods that return a custom ref struct. (
Span<T>/ReadOnlySpan<T>returns are supported.)
Parameter Predicates
When the method name is unique (no overloads), you can use argument matchers from the Match class for more flexible parameters matching:
Match.AnyParameters(): Matches any parameter combination.Match.Parameters(Func<object?[], bool> predicate): Matches parameters based on a custom predicate.
// Example: Custom parameter predicate
sut.Mock.Setup.Process(Match.Parameters(args =>
args.Length == 2 &&
args[0] is string s && s.StartsWith("test") &&
args[1] 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
int lastAmount = 0;
sut.Mock.Setup.Dispense(It.Is("Dark"), It.IsAny<int>().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
Mockolate.ParameterMonitor<int> monitor;
sut.Mock.Setup.Dispense(It.Is("Dark"), It.IsAny<int>().Monitor(out monitor));
sut.Dispense("Dark", 5);
sut.Dispense("Dark", 7);
// monitor.Values == [5, 7]