Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Docs/pages/02-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,18 @@ sut.SetupMock.Method.DispenseAsync(It.IsAny<string>(), It.IsAny<int>())
Mockolate provides flexible parameter matching for method setups and verifications:

- `It.IsAny<T>()`: Matches any value of type `T`.
- `It.Is<T>(predicate)`: Matches values based on a predicate.
- `It.Is<T>(value)`: Matches a specific value.
- `It.Is<T>(value)`: Matches a specific value. With `.Using(IEqualityComparer<T>)`, you can provide a custom equality comparer.
- `It.IsOneOf<T>(params T[] values)`: Matches any of the given values. With `.Using(IEqualityComparer<T>)`, you can provide a custom equality comparer.
- `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.IsOut<T>(…)`/`It.IsRef<T>(…)`: Matches and sets out/ref parameters, supports value setting and
- `It.IsInRange(min, max)`: Matches a number within the given range. You can append `.Exclusive()` to exclude the
minimum and maximum value.
- `It.IsOut<T>(…)`/`It.IsAnyOut<T>(…)`: Matches and sets out parameters, supports value setting and
predicates.
- `It.IsRef<T>(…)`/`It.IsAnyRef<T>(…)`: Matches and sets ref parameters, supports value setting and
predicates.
- `It.Matches<string>(pattern)`: Matches strings using wildcard patterns (`*` and `?`). With `.AsRegex()`, you can use regular expressions instead.
- `It.Satisfies<T>(predicate)`: Matches values based on a predicate.

#### Parameter Interaction

Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,18 @@ sut.SetupMock.Method.DispenseAsync(It.IsAny<string>(), It.IsAny<int>())
Mockolate provides flexible parameter matching for method setups and verifications:

- `It.IsAny<T>()`: Matches any value of type `T`.
- `It.Is<T>(predicate)`: Matches values based on a predicate.
- `It.Is<T>(value)`: Matches a specific value.
- `It.Is<T>(value)`: Matches a specific value. With `.Using(IEqualityComparer<T>)`, you can provide a custom equality comparer.
- `It.IsOneOf<T>(params T[] values)`: Matches any of the given values. With `.Using(IEqualityComparer<T>)`, you can provide a custom equality comparer.
- `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.IsOut<T>(…)`/`It.IsRef<T>(…)`: Matches and sets out/ref parameters, supports value setting and
- `It.IsOut<T>(…)`/`It.IsAnyOut<T>(…)`: Matches and sets out parameters, supports value setting and
predicates.
- `It.IsRef<T>(…)`/`It.IsAnyRef<T>(…)`: Matches and sets ref parameters, supports value setting and
predicates.
- `It.Matches<string>(pattern)`: Matches strings using wildcard patterns (`*` and `?`). With `.AsRegex()`, you can use regular expressions instead.
- `It.Satisfies<T>(predicate)`: Matches values based on a predicate.

#### Parameter Interaction

Expand Down
54 changes: 23 additions & 31 deletions Source/Mockolate/It.Is.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Mockolate.Internals;
using Mockolate.Parameters;

namespace Mockolate;
Expand All @@ -13,43 +11,43 @@ public partial class It
/// <summary>
/// Matches a parameter that is equal to <paramref name="value" />.
/// </summary>
public static IParameter<T> Is<T>(T value,
public static IIsParameter<T> Is<T>(T value,
[CallerArgumentExpression(nameof(value))]
string doNotPopulateThisValue = "")
=> new ParameterEqualsMatch<T>(value, doNotPopulateThisValue);

/// <summary>
/// Matches a parameter that is equal to <paramref name="value" /> according to the <paramref name="comparer" />.
/// An <see cref="IParameter{T}" /> used for equality comparison.
/// </summary>
public static IParameter<T> Is<T>(T value, IEqualityComparer<T> comparer,
[CallerArgumentExpression(nameof(value))]
string doNotPopulateThisValue1 = "",
[CallerArgumentExpression(nameof(comparer))]
string doNotPopulateThisValue2 = "")
=> new ParameterEqualsMatch<T>(value, doNotPopulateThisValue1, comparer, doNotPopulateThisValue2);

/// <summary>
/// Matches a parameter of type <typeparamref name="T" /> that satisfies the <paramref name="predicate" />.
/// </summary>
public static IParameter<T> Is<T>(Func<T, bool> predicate,
[CallerArgumentExpression("predicate")]
string doNotPopulateThisValue = "")
=> new PredicateParameterMatch<T>(predicate, doNotPopulateThisValue);
public interface IIsParameter<out T> : IParameter<T>
{
/// <summary>
/// Use the specified comparer to determine equality.
/// </summary>
IIsParameter<T> Using(IEqualityComparer<T> comparer,
[CallerArgumentExpression(nameof(comparer))]
string doNotPopulateThisValue = "");
}

private sealed class ParameterEqualsMatch<T> : TypedMatch<T>
private sealed class ParameterEqualsMatch<T> : TypedMatch<T>, IIsParameter<T>
{
private readonly IEqualityComparer<T>? _comparer;
private readonly string? _comparerExpression;
private readonly T _value;
private readonly string _valueExpression;
private IEqualityComparer<T>? _comparer;
private string? _comparerExpression;

public ParameterEqualsMatch(T value, string valueExpression, IEqualityComparer<T>? comparer = null,
string? comparerExpression = null)
public ParameterEqualsMatch(T value, string valueExpression)
{
_value = value;
_valueExpression = valueExpression;
}

/// <inheritdoc cref="IIsParameter{T}.Using(IEqualityComparer{T}, string)" />
public IIsParameter<T> Using(IEqualityComparer<T> comparer, string doNotPopulateThisValue = "")
{
_comparer = comparer;
_comparerExpression = comparerExpression;
_comparerExpression = doNotPopulateThisValue;
return this;
}

/// <inheritdoc cref="TypedMatch{T}.Matches(T)" />
Expand All @@ -68,18 +66,12 @@ public override string ToString()
{
if (_comparer is not null)
{
return $"It.Is({_valueExpression}, {_comparerExpression})";
return $"It.Is({_valueExpression}).Using({_comparerExpression})";
}

return _valueExpression;
}
}

private sealed class PredicateParameterMatch<T>(Func<T, bool> predicate, string predicateExpression) : TypedMatch<T>
{
protected override bool Matches(T value) => predicate(value);
public override string ToString() => $"It.Is<{typeof(T).FormatType()}>({predicateExpression})";
}
}
#pragma warning restore S3218 // Inner class members should not shadow outer class "static" or type members
#pragma warning restore S3453 // This class can't be instantiated; make its constructor 'public'.
42 changes: 38 additions & 4 deletions Source/Mockolate/It.IsOneOf.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Mockolate.Parameters;

namespace Mockolate;
Expand All @@ -11,21 +12,54 @@ public partial class It
/// <summary>
/// Matches a parameter that is equal to one of the <paramref name="values" />.
/// </summary>
public static IParameter<T> IsOneOf<T>(params T[] values)
public static IIsOneOfParameter<T> IsOneOf<T>(params T[] values)
=> new ParameterIsOneOfMatch<T>(values);

private sealed class ParameterIsOneOfMatch<T>(T[] values) : TypedMatch<T>
/// <summary>
/// An <see cref="IParameter{T}" /> used for equality comparison of a collection of alternatives.
/// </summary>
public interface IIsOneOfParameter<out T> : IParameter<T>
{
/// <summary>
/// Use the specified comparer to determine equality.
/// </summary>
IIsOneOfParameter<T> Using(IEqualityComparer<T> comparer,
[CallerArgumentExpression(nameof(comparer))]
string doNotPopulateThisValue = "");
}

private sealed class ParameterIsOneOfMatch<T>(T[] values) : TypedMatch<T>, IIsOneOfParameter<T>
{
private IEqualityComparer<T>? _comparer;
private string? _comparerExpression;

/// <inheritdoc cref="IIsParameter{T}.Using(IEqualityComparer{T}, string)" />
public IIsOneOfParameter<T> Using(IEqualityComparer<T> comparer, string doNotPopulateThisValue = "")
{
_comparer = comparer;
_comparerExpression = doNotPopulateThisValue;
return this;
}

/// <inheritdoc cref="TypedMatch{T}.Matches(T)" />
protected override bool Matches(T value)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
IEqualityComparer<T> comparer = _comparer ?? EqualityComparer<T>.Default;
return values.Any(v => comparer.Equals(value, v));
}

/// <inheritdoc cref="object.ToString()" />
public override string ToString()
=> $"It.IsOneOf({string.Join(", ", values.Select(v => v is string ? $"\"{v}\"" : v?.ToString() ?? "null"))})";
{
string result =
$"It.IsOneOf({string.Join(", ", values.Select(v => v is string ? $"\"{v}\"" : v?.ToString() ?? "null"))})";
if (_comparer is not null)
{
result += $".Using({_comparerExpression})";
}

return result;
}
}
}
#pragma warning restore S3218 // Inner class members should not shadow outer class "static" or type members
Expand Down
27 changes: 27 additions & 0 deletions Source/Mockolate/It.Satisfies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Runtime.CompilerServices;
using Mockolate.Internals;
using Mockolate.Parameters;

namespace Mockolate;

#pragma warning disable S3453 // This class can't be instantiated; make its constructor 'public'.
#pragma warning disable S3218 // Inner class members should not shadow outer class "static" or type members
public partial class It
{
/// <summary>
/// Matches a parameter of type <typeparamref name="T" /> that satisfies the <paramref name="predicate" />.
/// </summary>
public static IParameter<T> Satisfies<T>(Func<T, bool> predicate,
[CallerArgumentExpression("predicate")]
string doNotPopulateThisValue = "")
=> new SatisfiesPredicateMatch<T>(predicate, doNotPopulateThisValue);

private sealed class SatisfiesPredicateMatch<T>(Func<T, bool> predicate, string predicateExpression) : TypedMatch<T>
{
protected override bool Matches(T value) => predicate(value);
public override string ToString() => $"It.Satisfies<{typeof(T).FormatType()}>({predicateExpression})";
}
}
#pragma warning restore S3218 // Inner class members should not shadow outer class "static" or type members
#pragma warning restore S3453 // This class can't be instantiated; make its constructor 'public'.
15 changes: 11 additions & 4 deletions Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ namespace Mockolate
}
public class It
{
public static Mockolate.Parameters.IParameter<T> Is<T>(System.Func<T, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> Is<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> Is<T>(T value, System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue2 = "") { }
public static Mockolate.It.IIsParameter<T> Is<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> IsAny<T>() { }
public static Mockolate.Parameters.IOutParameter<T> IsAnyOut<T>() { }
public static Mockolate.Parameters.IVerifyReadOnlySpanParameter<T> IsAnyReadOnlySpan<T>() { }
Expand All @@ -40,7 +38,7 @@ namespace Mockolate
public static Mockolate.It.IInRangeParameter<T> IsInRange<T>(T minimum, T maximum, [System.Runtime.CompilerServices.CallerArgumentExpression("minimum")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("maximum")] string doNotPopulateThisValue2 = "")
where T : System.IComparable<T> { }
public static Mockolate.Parameters.IParameter<T> IsNull<T>() { }
public static Mockolate.Parameters.IParameter<T> IsOneOf<T>(params T[] values) { }
public static Mockolate.It.IIsOneOfParameter<T> IsOneOf<T>(params T[] values) { }
public static Mockolate.Parameters.IVerifyOutParameter<T> IsOut<T>() { }
public static Mockolate.Parameters.IOutParameter<T> IsOut<T>(System.Func<T> setter, [System.Runtime.CompilerServices.CallerArgumentExpression("setter")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IVerifyReadOnlySpanParameter<T> IsReadOnlySpan<T>(System.Func<T[], bool> predicate) { }
Expand All @@ -51,11 +49,20 @@ namespace Mockolate
public static Mockolate.Parameters.IVerifySpanParameter<T> IsSpan<T>(System.Func<T[], bool> predicate) { }
public static Mockolate.Parameters.IParameter<bool> IsTrue() { }
public static Mockolate.It.IParameterMatches Matches(string pattern) { }
public static Mockolate.Parameters.IParameter<T> Satisfies<T>(System.Func<T, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
public interface IInRangeParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.Parameters.IParameter<T> Exclusive();
Mockolate.Parameters.IParameter<T> Inclusive();
}
public interface IIsOneOfParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsOneOfParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IIsParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IParameterMatches : Mockolate.Parameters.IParameter<string>
{
Mockolate.It.IParameterMatches AsRegex(System.Text.RegularExpressions.RegexOptions options = 0, System.TimeSpan? timeout = default);
Expand Down
15 changes: 11 additions & 4 deletions Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ namespace Mockolate
}
public class It
{
public static Mockolate.Parameters.IParameter<T> Is<T>(System.Func<T, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> Is<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> Is<T>(T value, System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue2 = "") { }
public static Mockolate.It.IIsParameter<T> Is<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> IsAny<T>() { }
public static Mockolate.Parameters.IOutParameter<T> IsAnyOut<T>() { }
public static Mockolate.Parameters.IVerifyReadOnlySpanParameter<T> IsAnyReadOnlySpan<T>() { }
Expand All @@ -39,7 +37,7 @@ namespace Mockolate
public static Mockolate.It.IInRangeParameter<T> IsInRange<T>(T minimum, T maximum, [System.Runtime.CompilerServices.CallerArgumentExpression("minimum")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("maximum")] string doNotPopulateThisValue2 = "")
where T : System.IComparable<T> { }
public static Mockolate.Parameters.IParameter<T> IsNull<T>() { }
public static Mockolate.Parameters.IParameter<T> IsOneOf<T>(params T[] values) { }
public static Mockolate.It.IIsOneOfParameter<T> IsOneOf<T>(params T[] values) { }
public static Mockolate.Parameters.IVerifyOutParameter<T> IsOut<T>() { }
public static Mockolate.Parameters.IOutParameter<T> IsOut<T>(System.Func<T> setter, [System.Runtime.CompilerServices.CallerArgumentExpression("setter")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IVerifyReadOnlySpanParameter<T> IsReadOnlySpan<T>(System.Func<T[], bool> predicate) { }
Expand All @@ -50,11 +48,20 @@ namespace Mockolate
public static Mockolate.Parameters.IVerifySpanParameter<T> IsSpan<T>(System.Func<T[], bool> predicate) { }
public static Mockolate.Parameters.IParameter<bool> IsTrue() { }
public static Mockolate.It.IParameterMatches Matches(string pattern) { }
public static Mockolate.Parameters.IParameter<T> Satisfies<T>(System.Func<T, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
public interface IInRangeParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.Parameters.IParameter<T> Exclusive();
Mockolate.Parameters.IParameter<T> Inclusive();
}
public interface IIsOneOfParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsOneOfParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IIsParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IParameterMatches : Mockolate.Parameters.IParameter<string>
{
Mockolate.It.IParameterMatches AsRegex(System.Text.RegularExpressions.RegexOptions options = 0, System.TimeSpan? timeout = default);
Expand Down
Loading
Loading