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
89 changes: 89 additions & 0 deletions Source/Mockolate/It.IsNot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
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 that is not equal to <paramref name="value" />.
/// </summary>
public static IIsNotParameter<T> IsNot<T>(T value,
[CallerArgumentExpression(nameof(value))]
string doNotPopulateThisValue = "")
=> new ParameterEqualsNotMatch<T>(value, doNotPopulateThisValue);

/// <summary>
/// An <see cref="IParameter{T}" /> used for equality comparison.
/// </summary>
public interface IIsNotParameter<out T> : IParameter<T>
{
/// <summary>
/// Use the specified comparer to determine equality.
/// </summary>
IIsNotParameter<T> Using(IEqualityComparer<T> comparer,
[CallerArgumentExpression(nameof(comparer))]
string doNotPopulateThisValue = "");
}

[DebuggerNonUserCode]
private sealed class ParameterEqualsNotMatch<T> : TypedMatch<T>, IIsNotParameter<T>
{
private readonly T _value;
private readonly string _valueExpression;
private IEqualityComparer<T>? _comparer;
private string? _comparerExpression;

public ParameterEqualsNotMatch(T value, string valueExpression)
{
_value = value;
_valueExpression = valueExpression;
}

/// <inheritdoc cref="IIsNotParameter{T}.Using(IEqualityComparer{T}, string)" />
public IIsNotParameter<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)
{
if (_comparer is not null)
{
return !_comparer.Equals(value, _value);
}

return !EqualityComparer<T>.Default.Equals(value, _value);
}

public override bool Matches(object? value)
{
if (value is T typedValue)
{
return Matches(typedValue);
}

return value is not null || Matches(default!);
}
Comment thread
vbreuss marked this conversation as resolved.

/// <inheritdoc cref="object.ToString()" />
public override string ToString()
{
if (_comparer is not null)
{
return $"It.IsNot({_valueExpression}).Using({_comparerExpression})";
}

return $"It.IsNot({_valueExpression})";
}
}
}
#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'.
2 changes: 1 addition & 1 deletion Source/Mockolate/It.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private abstract class TypedMatch<T> : IParameter<T>, IParameter
/// <see langword="true" />, if the <paramref name="value" /> is a matching parameter
/// of type <typeparamref name="T" />; otherwise <see langword="false" />.
/// </returns>
public bool Matches(object? value)
public virtual bool Matches(object? value)
{
if (value is T typedValue)
{
Expand Down
5 changes: 5 additions & 0 deletions Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace Mockolate
public static Mockolate.Parameters.IParameter<bool> IsFalse() { }
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.It.IIsNotParameter<T> IsNot<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> IsNotNull<T>(string? toString = null) { }
public static Mockolate.It.IIsNotOneOfParameter<T> IsNotOneOf<T>(System.Collections.Generic.IEnumerable<T> values) { }
public static Mockolate.Parameters.IParameter<T> IsNull<T>(string? toString = null) { }
Expand All @@ -126,6 +127,10 @@ namespace Mockolate
{
Mockolate.It.IIsNotOneOfParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IIsNotParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsNotParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
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 = "");
Expand Down
5 changes: 5 additions & 0 deletions Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ namespace Mockolate
public static Mockolate.Parameters.IParameter<bool> IsFalse() { }
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.It.IIsNotParameter<T> IsNot<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> IsNotNull<T>(string? toString = null) { }
public static Mockolate.It.IIsNotOneOfParameter<T> IsNotOneOf<T>(System.Collections.Generic.IEnumerable<T> values) { }
public static Mockolate.Parameters.IParameter<T> IsNull<T>(string? toString = null) { }
Expand All @@ -125,6 +126,10 @@ namespace Mockolate
{
Mockolate.It.IIsNotOneOfParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IIsNotParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsNotParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
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 = "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ namespace Mockolate
public static Mockolate.Parameters.IParameter<bool> IsFalse() { }
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.It.IIsNotParameter<T> IsNot<T>(T value, [System.Runtime.CompilerServices.CallerArgumentExpression("value")] string doNotPopulateThisValue = "") { }
public static Mockolate.Parameters.IParameter<T> IsNotNull<T>(string? toString = null) { }
public static Mockolate.It.IIsNotOneOfParameter<T> IsNotOneOf<T>(System.Collections.Generic.IEnumerable<T> values) { }
public static Mockolate.Parameters.IParameter<T> IsNull<T>(string? toString = null) { }
Expand All @@ -112,6 +113,10 @@ namespace Mockolate
{
Mockolate.It.IIsNotOneOfParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
public interface IIsNotParameter<out T> : Mockolate.Parameters.IParameter<T>
{
Mockolate.It.IIsNotParameter<T> Using(System.Collections.Generic.IEqualityComparer<T> comparer, [System.Runtime.CompilerServices.CallerArgumentExpression("comparer")] string doNotPopulateThisValue = "");
}
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 = "");
Expand Down
136 changes: 136 additions & 0 deletions Tests/Mockolate.Tests/ItTests.IsNotTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using Mockolate.Parameters;

namespace Mockolate.Tests;

public sealed partial class ItTests
{
public sealed class IsNotTests
{
[Fact]
public async Task ShouldCorrectlyHandleNull()
{
IMyService sut = IMyService.CreateMock();
MyImplementation value1 = new();
sut.Mock.Setup.DoSomething(It.IsNot<IMyBase>(null!))
.Returns(3);

int result1 = sut.DoSomething(value1);
int result2 = sut.DoSomething(null!);

await That(result1).IsEqualTo(3);
await That(result2).IsEqualTo(0);
}
Comment thread
vbreuss marked this conversation as resolved.

[Fact]
public async Task ShouldCorrectlyHandleNullWithCovariance()
{
IMyService sut = IMyService.CreateMock();
MyOtherImplementation value1 = new();
sut.Mock.Setup.DoSomething(It.IsNot<MyImplementation>(null!))
.Returns(3);

int result1 = sut.DoSomething(value1);
int result2 = sut.DoSomething(null!);

await That(result1).IsEqualTo(3);
await That(result2).IsEqualTo(0);
}

[Theory]
[InlineData(1, true)]
[InlineData(5, false)]
[InlineData(-5, true)]
[InlineData(42, true)]
public async Task ShouldMatchWhenNotEqual(int value, bool expectMatch)
{
IParameter<int> sut = It.IsNot(5);

bool result = ((IParameter)sut).Matches(value);

await That(result).IsEqualTo(expectMatch);
}

[Fact]
public async Task ShouldSupportCovarianceInSetup()
{
IMyService sut = IMyService.CreateMock();
MyImplementation value1 = new();
MyOtherImplementation value2 = new();
sut.Mock.Setup.DoSomething(It.IsNot(value1))
.Returns(3);

int result1 = sut.DoSomething(value1);
int result2 = sut.DoSomething(value2);

await That(result1).IsEqualTo(0);
await That(result2).IsEqualTo(3);
}

[Fact]
public async Task ToString_ShouldReturnExpectedValue()
{
IParameter<string> sut = It.IsNot("foo");
string expectedValue = "It.IsNot(\"foo\")";

string? result = sut.ToString();

await That(result).IsEqualTo(expectedValue);
}

[Fact]
public async Task ToString_WithComparer_ShouldReturnExpectedValue()
{
IParameter<int> sut = It.IsNot(4).Using(new AllEqualComparer());
string expectedValue = "It.IsNot(4).Using(new AllEqualComparer())";

string? result = sut.ToString();

await That(result).IsEqualTo(expectedValue);
}

[Theory]
[InlineData(1)]
[InlineData(5)]
[InlineData(-42)]
public async Task WithComparer_ShouldUseComparer(int value)
{
IParameter<int> sut = It.IsNot(5).Using(new AllEqualComparer());

bool result = ((IParameter)sut).Matches(value);

await That(result).IsFalse();
}

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);
}
}
}