Skip to content

Instantly share code, notes, and snippets.

@haacked
Created January 14, 2012 07:25
Show Gist options
  • Save haacked/1610603 to your computer and use it in GitHub Desktop.
Save haacked/1610603 to your computer and use it in GitHub Desktop.
String Comparison Unit Test Helper
public static class TestHelpers
{
public static void ShouldEqualWithDiff(this string actualValue, string expectedValue)
{
ShouldEqualWithDiff(actualValue, expectedValue, DiffStyle.Full, Console.Out);
}
public static void ShouldEqualWithDiff(this string actualValue, string expectedValue, DiffStyle diffStyle)
{
ShouldEqualWithDiff(actualValue, expectedValue, diffStyle, Console.Out);
}
public static void ShouldEqualWithDiff(this string actualValue, string expectedValue, DiffStyle diffStyle, TextWriter output)
{
if(actualValue == null || expectedValue == null)
{
//Assert.AreEqual(expectedValue, actualValue);
Assert.Equal(expectedValue, actualValue);
return;
}
if (actualValue.Equals(expectedValue, StringComparison.Ordinal)) return;
output.WriteLine(" Idx Expected Actual");
output.WriteLine("-------------------------");
int maxLen = Math.Max(actualValue.Length, expectedValue.Length);
int minLen = Math.Min(actualValue.Length, expectedValue.Length);
for (int i = 0; i < maxLen; i++)
{
if (diffStyle != DiffStyle.Minimal || i >= minLen || actualValue[i] != expectedValue[i])
{
output.WriteLine("{0} {1,-3} {2,-4} {3,-3} {4,-4} {5,-3}",
i < minLen && actualValue[i] == expectedValue[i] ? " " : "*", // put a mark beside a differing row
i, // the index
i < expectedValue.Length ? ((int)expectedValue[i]).ToString() : "", // character decimal value
i < expectedValue.Length ? expectedValue[i].ToSafeString() : "", // character safe string
i < actualValue.Length ? ((int)actualValue[i]).ToString() : "", // character decimal value
i < actualValue.Length ? actualValue[i].ToSafeString() : "" // character safe string
);
}
}
output.WriteLine();
//Assert.AreEqual(expectedValue, actualValue);
Assert.Equal(expectedValue, actualValue);
}
private static string ToSafeString(this char c)
{
if (Char.IsControl(c) || Char.IsWhiteSpace(c))
{
switch (c)
{
case '\r':
return @"\r";
case '\n':
return @"\n";
case '\t':
return @"\t";
case '\a':
return @"\a";
case '\v':
return @"\v";
case '\f':
return @"\f";
default:
return String.Format("\\u{0:X};", (int)c);
}
}
return c.ToString(CultureInfo.InvariantCulture);
}
}
public enum DiffStyle
{
Full,
Minimal
}
@rplantiko
Copy link

Not knowing C#, I learned with your example the meaning of the prefix @ before a string literal in C# = no escaped char sequences.

@haacked
Copy link
Author

haacked commented Jan 14, 2012

@jarshwah Thanks! Are you sure that makes a difference? There are many cases where the C# compiler can optimize calls like this. For example, since it knows that actualValue and expectedValue don't change, it can evaluate the length and max once. I don't know if it does it in this particular case, but would be interesting to check the IL and measure it to see if it really does make a difference. :)

@sinairv
Copy link

sinairv commented Jan 15, 2012

I forked and made some modifications to your snippet, here it is: https://gist.github.com/1615334

PS. how can I send a pull request for a gist?

@haacked
Copy link
Author

haacked commented Jan 15, 2012

@sinairv thanks! I'll take a look at it. Unfortunately, gistts don't support pull requests.

@af4jm
Copy link

af4jm commented Jan 17, 2012

@haacked & @sinairv, for readability, I would suggest removing the call to string.Format & using the overload output.WriteLine(string format, params object[] args)

output.WriteLine("{0} {1,-3} {2,-4} {3,-3}  {4,-4} {5,-3}",
    i < minLen && actual[i] == expected[i] ? " " : "*", // put a mark beside a differing row
    i, // the index
    i < expected.Length ? ((int)expected[i]).ToString() : "", // character decimal value
    i < expected.Length ? expected[i].ToSafeString() : "", // character safe string
    i < actual.Length ? ((int)actual[i]).ToString() : "", // character decimal value
    i < actual.Length ? actual[i].ToSafeString() : "" // character safe string
);

@haacked
Copy link
Author

haacked commented Jan 21, 2012

@dotnetzebra good suggestion. I made the change.

@jplindgren
Copy link

Thx man.
This helper class help me A LOT with some tricky tests.

@rhyous
Copy link

rhyous commented Mar 22, 2016

I like this. Helped me out a lot.

Maybe consider creating a NuGet package just for this source file. You can create NuGet packages for source pretty easily. I am doing just that now with common files I used to drag around.
http://www.rhyous.com/2016/03/03/nuget-for-source-using-add-as-link-1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment