diff --git a/Source/Mockolate/It.IsOneOf.cs b/Source/Mockolate/It.IsOneOf.cs new file mode 100644 index 00000000..24aadcdb --- /dev/null +++ b/Source/Mockolate/It.IsOneOf.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; +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 +{ + /// + /// Matches a parameter that is equal to one of the . + /// + public static IParameter IsOneOf(params T[] values) + => new ParameterIsOneOfMatch(values); + + private sealed class ParameterIsOneOfMatch(T[] values) : TypedMatch + { + /// + protected override bool Matches(T value) + { + EqualityComparer comparer = EqualityComparer.Default; + return values.Any(v => comparer.Equals(value, v)); + } + + /// + public override string ToString() + => $"It.IsOneOf({string.Join(", ", values.Select(v => v is string ? $"\"{v}\"" : v?.ToString() ?? "null"))})"; + } +} +#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'. diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt index 99d71074..c26f0c44 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt @@ -40,6 +40,7 @@ namespace Mockolate public static Mockolate.It.IInRangeParameter IsInRange(T minimum, T maximum, [System.Runtime.CompilerServices.CallerArgumentExpression("minimum")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("maximum")] string doNotPopulateThisValue2 = "") where T : System.IComparable { } public static Mockolate.Parameters.IParameter IsNull() { } + public static Mockolate.Parameters.IParameter IsOneOf(params T[] values) { } public static Mockolate.Parameters.IVerifyOutParameter IsOut() { } public static Mockolate.Parameters.IOutParameter IsOut(System.Func setter, [System.Runtime.CompilerServices.CallerArgumentExpression("setter")] string doNotPopulateThisValue = "") { } public static Mockolate.Parameters.IVerifyReadOnlySpanParameter IsReadOnlySpan(System.Func predicate) { } diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt index 760c254d..ec7b2fb0 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt @@ -39,6 +39,7 @@ namespace Mockolate public static Mockolate.It.IInRangeParameter IsInRange(T minimum, T maximum, [System.Runtime.CompilerServices.CallerArgumentExpression("minimum")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("maximum")] string doNotPopulateThisValue2 = "") where T : System.IComparable { } public static Mockolate.Parameters.IParameter IsNull() { } + public static Mockolate.Parameters.IParameter IsOneOf(params T[] values) { } public static Mockolate.Parameters.IVerifyOutParameter IsOut() { } public static Mockolate.Parameters.IOutParameter IsOut(System.Func setter, [System.Runtime.CompilerServices.CallerArgumentExpression("setter")] string doNotPopulateThisValue = "") { } public static Mockolate.Parameters.IVerifyReadOnlySpanParameter IsReadOnlySpan(System.Func predicate) { } diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt index 30d01e37..6ed02460 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt @@ -36,6 +36,7 @@ namespace Mockolate public static Mockolate.It.IInRangeParameter IsInRange(T minimum, T maximum, [System.Runtime.CompilerServices.CallerArgumentExpression("minimum")] string doNotPopulateThisValue1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression("maximum")] string doNotPopulateThisValue2 = "") where T : System.IComparable { } public static Mockolate.Parameters.IParameter IsNull() { } + public static Mockolate.Parameters.IParameter IsOneOf(params T[] values) { } public static Mockolate.Parameters.IVerifyOutParameter IsOut() { } public static Mockolate.Parameters.IOutParameter IsOut(System.Func setter, [System.Runtime.CompilerServices.CallerArgumentExpression("setter")] string doNotPopulateThisValue = "") { } public static Mockolate.Parameters.IVerifyRefParameter IsRef() { } diff --git a/Tests/Mockolate.Tests/MatchTests.AnyOutTests.cs b/Tests/Mockolate.Tests/ItTests.IsAnyOutTests.cs similarity index 90% rename from Tests/Mockolate.Tests/MatchTests.AnyOutTests.cs rename to Tests/Mockolate.Tests/ItTests.IsAnyOutTests.cs index 7d659ff9..0639a06a 100644 --- a/Tests/Mockolate.Tests/MatchTests.AnyOutTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsAnyOutTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class AnyOutTests + public sealed class IsAnyOutTests { [Theory] [InlineData(42L, false)] diff --git a/Tests/Mockolate.Tests/MatchTests.AnyParametersTests.cs b/Tests/Mockolate.Tests/ItTests.IsAnyParametersTests.cs similarity index 88% rename from Tests/Mockolate.Tests/MatchTests.AnyParametersTests.cs rename to Tests/Mockolate.Tests/ItTests.IsAnyParametersTests.cs index 0574ef67..24675234 100644 --- a/Tests/Mockolate.Tests/MatchTests.AnyParametersTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsAnyParametersTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class AnyParametersTests + public sealed class IsAnyParametersTests { [Theory] [InlineData(null, null)] diff --git a/Tests/Mockolate.Tests/MatchTests.AnyRefTests.cs b/Tests/Mockolate.Tests/ItTests.IsAnyRefTests.cs similarity index 90% rename from Tests/Mockolate.Tests/MatchTests.AnyRefTests.cs rename to Tests/Mockolate.Tests/ItTests.IsAnyRefTests.cs index 14563a4d..5db059a1 100644 --- a/Tests/Mockolate.Tests/MatchTests.AnyRefTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsAnyRefTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class AnyRefTests + public sealed class IsAnyRefTests { [Theory] [InlineData(42L, false)] diff --git a/Tests/Mockolate.Tests/MatchTests.AnyTests.cs b/Tests/Mockolate.Tests/ItTests.IsAnyTests.cs similarity index 89% rename from Tests/Mockolate.Tests/MatchTests.AnyTests.cs rename to Tests/Mockolate.Tests/ItTests.IsAnyTests.cs index 3e343bff..a6c773e0 100644 --- a/Tests/Mockolate.Tests/MatchTests.AnyTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsAnyTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class AnyTests + public sealed class IsAnyTests { [Theory] [InlineData(null)] diff --git a/Tests/Mockolate.Tests/MatchTests.FalseTests.cs b/Tests/Mockolate.Tests/ItTests.IsFalseTests.cs similarity index 90% rename from Tests/Mockolate.Tests/MatchTests.FalseTests.cs rename to Tests/Mockolate.Tests/ItTests.IsFalseTests.cs index e86f552e..953a1738 100644 --- a/Tests/Mockolate.Tests/MatchTests.FalseTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsFalseTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class FalseTests + public sealed class IsFalseTests { [Theory] [InlineData(false, 1)] diff --git a/Tests/Mockolate.Tests/MatchTests.InRangeTests.cs b/Tests/Mockolate.Tests/ItTests.IsInRangeTests.cs similarity index 96% rename from Tests/Mockolate.Tests/MatchTests.InRangeTests.cs rename to Tests/Mockolate.Tests/ItTests.IsInRangeTests.cs index d66f1b74..f445e895 100644 --- a/Tests/Mockolate.Tests/MatchTests.InRangeTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsInRangeTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class InRangeTests + public sealed class IsInRangeTests { [Theory] [InlineData(3, 5, false)] diff --git a/Tests/Mockolate.Tests/MatchTests.NullTests.cs b/Tests/Mockolate.Tests/ItTests.IsNullTests.cs similarity index 63% rename from Tests/Mockolate.Tests/MatchTests.NullTests.cs rename to Tests/Mockolate.Tests/ItTests.IsNullTests.cs index d55f5873..04f0c51b 100644 --- a/Tests/Mockolate.Tests/MatchTests.NullTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsNullTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class NullTests + public sealed class IsNullTests { [Theory] [InlineData(null, 1)] @@ -29,5 +29,18 @@ public async Task ToString_ShouldReturnExpectedValue() await That(result).IsEqualTo(expectedValue); } + + [Theory] + [InlineData(null, true)] + [InlineData("", false)] + [InlineData("foo", false)] + public async Task WithValue_Nullable_ShouldMatchWhenEqual(string? value, bool expectMatch) + { + IParameter sut = It.IsNull(); + + bool result = ((IParameter)sut).Matches(value); + + await That(result).IsEqualTo(expectMatch); + } } } diff --git a/Tests/Mockolate.Tests/ItTests.IsOneOfTests.cs b/Tests/Mockolate.Tests/ItTests.IsOneOfTests.cs new file mode 100644 index 00000000..f571fedc --- /dev/null +++ b/Tests/Mockolate.Tests/ItTests.IsOneOfTests.cs @@ -0,0 +1,118 @@ +using Mockolate.Parameters; + +namespace Mockolate.Tests; + +public sealed partial class ItTests +{ + public sealed class IsOneOfTests + { + [Fact] + public async Task ToString_WithNullableIntValues_ShouldReturnExpectedValue() + { + IParameter sut = It.IsOneOf(3, null, 5); + string expectedValue = "It.IsOneOf(3, null, 5)"; + + string? result = sut.ToString(); + + await That(result).IsEqualTo(expectedValue); + } + + [Fact] + public async Task ToString_WithStringValues_ShouldReturnExpectedValue() + { + IParameter sut = It.IsOneOf("foo", "bar"); + string expectedValue = "It.IsOneOf(\"foo\", \"bar\")"; + + string? result = sut.ToString(); + + await That(result).IsEqualTo(expectedValue); + } + + [Theory] + [InlineData(1, false)] + [InlineData(4, false)] + [InlineData(5, true)] + [InlineData(6, true)] + [InlineData(7, true)] + [InlineData(8, false)] + [InlineData(-5, false)] + [InlineData(42, false)] + public async Task WithValue_ShouldMatchWhenEqualToAny(int value, bool expectMatch) + { + IParameter sut = It.IsOneOf(5, 6, 7); + + bool result = ((IParameter)sut).Matches(value); + + await That(result).IsEqualTo(expectMatch); + } + + [Fact] + public async Task WithValue_ShouldSupportCovarianceInSetup() + { + IMyService mock = Mock.Create(); + MyImplementation value1 = new(); + MyImplementation value2 = new(); + MyOtherImplementation other1 = new(); + mock.SetupMock.Method.DoSomething(It.IsOneOf(value1, value2)) + .Returns(3); + + int result1 = mock.DoSomething(value1); + int result2 = mock.DoSomething(other1); + + await That(result1).IsEqualTo(3); + await That(result2).IsEqualTo(0); + } + + [Fact] + public async Task WithValue_ShouldSupportCovarianceInVerify() + { + IMyService mock = Mock.Create(); + mock.SetupMock.Method.DoSomething(It.Is(_ => true)) + .Do(d => d.DoWork()) + .Returns(3); + MyImplementation value1 = new(); + MyImplementation value2 = new(); + MyOtherImplementation other1 = new(); + MyOtherImplementation other2 = new(); + + int result1 = mock.DoSomething(value1); + + await That(mock.VerifyMock.Invoked.DoSomething(It.IsOneOf(value1, value2))).Once(); + await That(value1.Progress).IsEqualTo(1); + await That(result1).IsEqualTo(3); + await That(mock.VerifyMock.Invoked.DoSomething(It.IsOneOf(other1, other2))).Never(); + } + + public interface IMyBase + { + int DoWork(); + } + + public class MyImplementation : IMyBase + { + public int Progress { get; private set; } + + public int DoWork() + { + Progress++; + return Progress; + } + } + + public class MyOtherImplementation : IMyBase + { + public string Output { get; private set; } = ""; + + public int DoWork() + { + Output += "did something\n"; + return 1; + } + } + + public interface IMyService + { + int DoSomething(IMyBase value); + } + } +} diff --git a/Tests/Mockolate.Tests/MatchTests.OutTests.cs b/Tests/Mockolate.Tests/ItTests.IsOutTests.cs similarity index 94% rename from Tests/Mockolate.Tests/MatchTests.OutTests.cs rename to Tests/Mockolate.Tests/ItTests.IsOutTests.cs index f4b5a9d5..237b6bd3 100644 --- a/Tests/Mockolate.Tests/MatchTests.OutTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsOutTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class OutTests + public sealed class IsOutTests { [Fact] public async Task ToString_ShouldReturnExpectedValue() diff --git a/Tests/Mockolate.Tests/MatchTests.RefTests.cs b/Tests/Mockolate.Tests/ItTests.IsRefTests.cs similarity index 97% rename from Tests/Mockolate.Tests/MatchTests.RefTests.cs rename to Tests/Mockolate.Tests/ItTests.IsRefTests.cs index d55327d5..44de174d 100644 --- a/Tests/Mockolate.Tests/MatchTests.RefTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsRefTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class RefTests + public sealed class IsRefTests { [Fact] public async Task ToString_ShouldReturnExpectedValue() diff --git a/Tests/Mockolate.Tests/MatchTests.WithTests.cs b/Tests/Mockolate.Tests/ItTests.IsTests.cs similarity index 92% rename from Tests/Mockolate.Tests/MatchTests.WithTests.cs rename to Tests/Mockolate.Tests/ItTests.IsTests.cs index 52930624..637f8693 100644 --- a/Tests/Mockolate.Tests/MatchTests.WithTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class WithTests + public sealed class IsTests { [Fact] public async Task ToString_WithPredicate_ShouldReturnExpectedValue() @@ -109,19 +109,6 @@ public async Task WithPredicate_ShouldSupportCovarianceInVerify() await That(mock.VerifyMock.Invoked.DoSomething(It.Is(_ => true))).Never(); } - [Theory] - [InlineData(null, true)] - [InlineData("", false)] - [InlineData("foo", false)] - public async Task WithValue_Nullable_ShouldMatchWhenEqual(string? value, bool expectMatch) - { - IParameter sut = It.IsNull(); - - bool result = ((IParameter)sut).Matches(value); - - await That(result).IsEqualTo(expectMatch); - } - [Theory] [InlineData(1, false)] [InlineData(5, true)] diff --git a/Tests/Mockolate.Tests/MatchTests.TrueTests.cs b/Tests/Mockolate.Tests/ItTests.IsTrueTests.cs similarity index 91% rename from Tests/Mockolate.Tests/MatchTests.TrueTests.cs rename to Tests/Mockolate.Tests/ItTests.IsTrueTests.cs index 5e1fe3c8..fc62d03d 100644 --- a/Tests/Mockolate.Tests/MatchTests.TrueTests.cs +++ b/Tests/Mockolate.Tests/ItTests.IsTrueTests.cs @@ -2,9 +2,9 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { - public sealed class TrueTests + public sealed class IsTrueTests { [Fact] public async Task ToString_ShouldReturnExpectedValue() diff --git a/Tests/Mockolate.Tests/MatchTests.MatchesTests.cs b/Tests/Mockolate.Tests/ItTests.MatchesTests.cs similarity index 98% rename from Tests/Mockolate.Tests/MatchTests.MatchesTests.cs rename to Tests/Mockolate.Tests/ItTests.MatchesTests.cs index ef78f71b..c3fbf302 100644 --- a/Tests/Mockolate.Tests/MatchTests.MatchesTests.cs +++ b/Tests/Mockolate.Tests/ItTests.MatchesTests.cs @@ -3,7 +3,7 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { public sealed class MatchesTests { diff --git a/Tests/Mockolate.Tests/MatchTests.cs b/Tests/Mockolate.Tests/ItTests.cs similarity index 98% rename from Tests/Mockolate.Tests/MatchTests.cs rename to Tests/Mockolate.Tests/ItTests.cs index 3d661242..76b8bc3b 100644 --- a/Tests/Mockolate.Tests/MatchTests.cs +++ b/Tests/Mockolate.Tests/ItTests.cs @@ -5,7 +5,7 @@ namespace Mockolate.Tests; -public sealed partial class MatchTests +public sealed partial class ItTests { [Fact] public async Task InvokeCallbacks_WithCorrectType_ShouldInvokeCallback()