Skip to content

Commit

Permalink
Improve String Equals Assertion Message
Browse files Browse the repository at this point in the history
  • Loading branch information
thomhurst committed Oct 7, 2024
1 parent 963de75 commit d9bb79a
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 35 deletions.
2 changes: 1 addition & 1 deletion TUnit.Assertions.UnitTests/EqualsAssertionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public async Task Assertion_Message_Has_Correct_Expression()

await NUnitAssert.ThatAsync(async () =>
await TUnitAssert.That(one).IsEqualTo("2", StringComparison.Ordinal).And.IsNotEqualTo("1").And.IsTypeOf(typeof(string)),
Throws.Exception.Message.Contain("Assert.That(one).IsEqualTo(\"2\", StringComparison.Ordinal).And.IsNotEqualTo(\"1\", StringComparison.Ordinal).And.IsTypeOf(String)")
Throws.Exception.Message.Contain("Assert.That(one).IsEqualTo(\"2\", StringComparison.Ordinal).And.IsNotEqualTo(\"1\", StringComparison.Ord...")
);
}

Expand Down
56 changes: 56 additions & 0 deletions TUnit.Assertions.UnitTests/StringEqualsAssertionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,60 @@ public void Equals_NullAndEmptyEquality2_Failure()

NUnitAssert.ThrowsAsync<TUnitAssertionException>(async () => await TUnitAssert.That(value1).IsEqualTo(value2).WithNullAndEmptyEquality());
}

[Test]
public void Equals_Failure_Long_Message()
{
var value1 = """
Lorem ipsum dolor sit amet diam duo amet sea rebum.
Et voluptua ex voluptua no praesent diam eu sed consetetur sit at ipsum et consetetur aliquam ipsum dolor.
Et et sit nulla hendrerit ipsum stet ut quod rebum.
Facer kasd et dolor ea justo.
Nulla qui ut takimata sadipscing sanctus magna et aliquip sed lorem rebum rebum nonumy dolores kasd ipsum ipsum sea.
Iriure et augue feugiat eirmod et et rebum placerat gubergren nulla voluptua illum.
Rebum sea aliquyam illum eos dolores duo justo dolor sit lorem et eu stet amet amet.
Consequat odio ea veniam.
Amet enim in gubergren stet rebum consetetur nonumy eirmod elitr.
Dolores ex clita voluptua et magna dolor justo clita dolor erat sed erat.
Cum sadipscing sit tempor dolore elitr.
Ullamcorper ipsum erat labore esse diam et tation magna elitr lorem est eirmod lorem ad dignissim ipsum.
Et duo et elit.
Aliquyam dolores sed elitr sit diam sed stet diam diam.
Erat ea vero blandit elitr sea hendrerit aliquyam sanctus lobortis ipsum clita.
Eu magna dolores justo kasd aliquyam augue et sed ipsum et stet dolores aliquyam et eos erat diam duo.
Quis duo feugait erat diam. Amet minim vero veniam esse consequat tation takimata eu in diam ut ea hendrerit eos gubergren ea eirmod.
Volutpat vero est ea clita clita magna dolor nulla ipsum aliquyam nonumy.
""";

var value2 = """
Lorem ipsum dolor sit amet diam duo amet sea rebum.
Et voluptua ex voluptua no praesent diam eu sed consetetur sit at ipsum et consetetur aliquam ipsum dolor.
Et et sit nulla hendrerit ipsum stet ut quod rebum.
Facer kasd et dolor ea justo.
Nulla qui ut takimata sadipscing sanctus magna et aliquip sed lorem rebum rebum nonumy dolores kasd ipsum ipsum sea.
Iriure et augue feugiat eirmod et et rebum placerat gubergren nulla voluptua illum.
Rebum sea aliquyam illum eos dolores duo justo dolor sit lorem et eu stet amet amet.
Consequat odio ea veniam!
Amet enim in gubergren stet rebum consetetur nonumy eirmod elitr.
Dolores ex clita voluptua et magna dolor justo clita dolor erat sed erat.
Cum sadipscing sit tempor dolore elitr.
Ullamcorper ipsum erat labore esse diam et tation magna elitr lorem est eirmod lorem ad dignissim ipsum.
Et duo et elit.
Aliquyam dolores sed elitr sit diam sed stet diam diam.
Erat ea vero blandit elitr sea hendrerit aliquyam sanctus lobortis ipsum clita.
Eu magna dolores justo kasd aliquyam augue et sed ipsum et stet dolores aliquyam et eos erat diam duo.
Quis duo feugait erat diam. Amet minim vero veniam esse consequat tation takimata eu in diam ut ea hendrerit eos gubergren ea eirmod.
Volutpat vero est ea clita clita magna dolor nulla ipsum aliquyam nonumy.
""";

var exception = NUnitAssert.ThrowsAsync<TUnitAssertionException>(async () => await TUnitAssert.That(value1).IsEqualTo(value2));
NUnitAssert.That(exception!.Message, Is.EqualTo("""
Assert.That(value1).IsEqualTo(value2, StringComparison.Ordinal)
Difference at index 563:
"Consequat odio ea veniam. Amet enim in gubergren s..."
^
"Consequat odio ea veniam! Amet enim in gubergren s..."
^
"""));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace TUnit.Assertions.AssertConditions.String;
using TUnit.Assertions.Extensions;

namespace TUnit.Assertions.AssertConditions.String;

public class StringEqualsExpectedValueAssertCondition(string expected, StringComparison stringComparison)
: ExpectedValueAssertCondition<string, string>(expected)
Expand All @@ -18,46 +20,46 @@ protected override bool Passes(string? actualValue, string? expectedValue)
return string.Equals(actualValue, expectedValue, stringComparison);
}

protected override string GetFailureMessage(string? actualValue, string? expectedValue) => $"""
Expected: {Format(ExpectedValue)}
Received: {Format(ActualValue)}
{GetLocation()}
""";

private string GetLocation()
protected override string GetFailureMessage(string? actualValue, string? expectedValue)
{
var longest = Math.Max(ActualValue?.Length ?? 0, ExpectedValue?.Length ?? 0);

var errorIndex = -1;
for (var i = 0; i < longest; i++)
if (actualValue?.Length <= 100 && expectedValue?.Length <= 100)
{
var actualCharacter = ActualValue?.ElementAtOrDefault(i);
var expectedCharacter = ExpectedValue?.ElementAtOrDefault(i);

if (actualCharacter != expectedCharacter)
{
errorIndex = i;
break;
}
return $"""
Expected: {Format(ExpectedValue)}
Received: {Format(ActualValue)}
{GetLocation(actualValue, expectedValue)}
""";
}

return GetLocation(actualValue, expectedValue);
}

if (errorIndex == -1)
{
return string.Empty;
}
private string GetLocation(string? actualValue, string? expectedValue)
{
var initialIndexOfDifference = StringUtils.IndexOfDifference(actualValue, expectedValue);

var startIndex = Math.Max(0, errorIndex - 10);
var startIndex = Math.Max(0, initialIndexOfDifference - 25);

var actualLine = actualValue
?.Substring(startIndex, Math.Min(actualValue.Length - startIndex, 55))
.ReplaceNewLines()
.Trim()
.TruncateWithEllipsis(50) ?? string.Empty;

var spacesPrecedingArrow = errorIndex - startIndex;
var expectedLine = expectedValue
?.Substring(startIndex, Math.Min(expectedValue.Length - startIndex, 55))
.ReplaceNewLines()
.Trim()
.TruncateWithEllipsis(50) ?? string.Empty;

var spacesBeforeArrow = StringUtils.IndexOfDifference(actualLine, expectedLine) + 1;

return $"""
Difference at index {errorIndex}:
{ActualValue?.Substring(startIndex, Math.Min(ActualValue!.Length - startIndex, 20))}
{new string(' ', spacesPrecedingArrow)}^
{ExpectedValue?.Substring(startIndex, Math.Min(ExpectedValue!.Length - startIndex, 20))}
{new string(' ', spacesPrecedingArrow)}^
Difference at index {initialIndexOfDifference}:
{Format(actualLine)}
{new string(' ', spacesBeforeArrow)}^
{Format(expectedLine)}
{new string(' ', spacesBeforeArrow)}^
""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ async IAsyncEnumerable<BaseAssertCondition> IInvokableAssertionBuilder.GetFailur

string? IInvokableAssertionBuilder.GetExpression()
{
return ExpressionBuilder?.ToString();
var expression = ExpressionBuilder?.ToString();

if (expression?.Length < 100)
{
return expression;
}

return $"{expression?[..100]}...";
}
}
15 changes: 15 additions & 0 deletions TUnit.Assertions/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,19 @@ public static string GetStringOr(this string? value, string defaultValue)
{
return value ?? defaultValue;
}

public static string ReplaceNewLines(this string value)
{
return value.ReplaceLineEndings(" ");
}

public static string TruncateWithEllipsis(this string value, int maxLength)
{
if (value.Length <= maxLength)
{
return value;
}

return $"{value[..maxLength]}...";
}
}
19 changes: 19 additions & 0 deletions TUnit.Assertions/StringUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,23 @@ internal static class StringUtils

return string.Join(string.Empty, input.Where(c=>!char.IsWhiteSpace(c)));
}

public static int IndexOfDifference(string? value1, string? value2)
{
if (value1 == value2)
{
return -1;
}

if (value1 is null || value2 is null)
{
return 0;
}

return value1
.Zip(value2, (item, updated) => (Item: item, Updated: updated))
.Select((pair, index) => (Pair: pair, Index: index))
.FirstOrDefault(x => x.Pair.Item != x.Pair.Updated)
.Index;
}
}

0 comments on commit d9bb79a

Please sign in to comment.