Musing, Rants & Jumbled Thoughts

Header Photo Credit: Lorenzo Cafaro (Creative Commons Zero License)

When writing unit tests, having assertions is a fundamental requirement. They are, ultimately, the "test" part of a unit test. But many developers are unfamiliar with the assertion libraries that come with the popular unit testing frameworks, so don't get the full range of their benefits. Too many times in my career, I've seen developer exclusively utilize Assert.True(...) with some conditional check inside it. And while this will fail the test when the conditional is no longer met, all you get from the failure is a message that says:

Expected: True
But was:  False
Which means to even figure out what's wrong, you have to, at minimum, look at the unit test code to see what it was even testing.

Photo by Kevin Ku on Unsplash

My hope is that, after reading through this post (and bookmarking it for frequent reference), you'll be able to write unit tests that easily provide you with enough detail in the failure messages that you can make some educated guesses about why it's failing without ever looking at the code.

For example, here's some NUnit failure messages -- can you guess what the test was attempting to validate?

Expected: 100 +/- 8 Percent
But was:  91
Expected: equivalent to < "bar", "baz" >
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >
Extra (3): < "foo" , "bin" , <string.Empty> >

In this post, I cover the assertion options and syntax for each of the three most popular .NET unit testing frameworks: NUnit, xUnit and MSTest. Plus, I throw in a few personal opinions about the frameworks along the way.

The Framework Options

Here are the three frameworks I'm going to focus on in this post:

Personally, I greatly prefer the NUnit Constraint-style asserts and will push to use NUnit on any project I can because of this. NUnit's Constraint-style assert syntax is the easiest to read, has the most useful out-of-the-box failure messages (particularly for dealing with collections), and has more features built-in than the other options.

xUnit.net has become the preferred testing framework for the Microsoft .NET codebase, and the framework itself has some nice features, but it still lags a bit behind NUnit in my opinion.

MSTest is easily my least favorite, as it has the worst documentation, the least features, and the syntax is a bit clunky. But it has the huge benefit of being built-in to Visual Studio, so takes the least effort to get up-and-running, though NuGet packages make the other two extremely simple as well.

All three frameworks support both the .NET Full Framework and .NET Core/netstandard, so you shouldn't have any issues using them across all your projects.

How Asserts Work

The implementation for asserts are pretty straightforward. Unit tests will fail if an unexpected and uncaught Exception occurs. The failure message reported by the testing framework is the Exception.Message value. (Plus, most framework runners will also show you the full stacktrace of the Exception).

So Asserts are just shortcuts for throwing an Exception when a comparison isn't true. In most cases, the Assert methods will take in an "expected" value (ie: what you want the result to be if your code is working correctly), an "actual" value (ie: the value your code actually generated), and an optional "message". Given those inputs, the Assert method will compare the actual value to the expected value and if the comparison fails, it will generated an Exception with a Message field that contains some hopefully useful information about the actual and expected values and the comparison attempted, as well as the additional "message" if you provided one.

So if you take the case of Assert.That(actual, Is.GreaterThan(5)), you could just as well write this code to get the same result:
if (actual < 5) throw new Exception("Expected a value greater than 5, but got " + actual)

Syntax Examples

When it comes to syntax, the NUnit Constraint-style syntax is different than the others -- something I think makes it much more readable and usable.

xUnit.net, MSTest and the NUnit Classic-style assertions all follow the pattern of Assert.Something(expectedValue, actualValue). The NUnit Constraint-style syntax attempts to read more like an English-language sentence, like Assert.That(actualValue, Is.SomethingTo(expectedValue)).

A word about custom messages

All of the assertion options allow you to provide a custom message to show in addition to the assertion's own output upon failure. This message optional but is the most effective way of providing useful output when your tests fail, since you can add whatever data you deem important at the time you're writing the test.

But, we're all generally pretty lazy when it comes to writing unit tests, and in my experience we'll only include a custom message on rare occasions. The rest of the time, we rely on the default output from the assertions themselves.

In those cases where you do want to provide your own messages, the assertion methods take a final string parameter that you can use to provide that message, like this: Assert.GreaterOrEqual(0, price, "Price should never be less than 0");. This way your output can explain the failure with output like this:

Price should never be less than 0
Expected: greater than or equal to 0
But was:  -2.52

Equality Checks

This first set of equality checks are effectively checking object.Equals() to determine if the two values are equal.

/// <summary>
/// Assertions that test for equality using object.Equals() to compare the actual value
/// to the expected value
/// </summary>
[Test]
public void EqualityChecks()
{

    bool valueToTest_bool = true;
    string valueToTest_string = "some result";
    DateTime valueToTest_datetime = new DateTime(2019, 01, 01);

    var valueToTest_obj = new { Foo = "bar", Baz = true };
    var expectedValue_obj_equal = new { Foo = "bar", Baz = true };
    var expectedValue_obj_notequal = new { Foo = "zoom", Baz = false }; ;


    Assert.That(valueToTest_bool, Is.EqualTo(true));
    Assert.That(valueToTest_string, Is.EqualTo("some result"));
    Assert.That(valueToTest_datetime, Is.EqualTo(new DateTime(2019, 01, 01)));
    Assert.That(valueToTest_obj, Is.EqualTo(expectedValue_obj_equal));

    Assert.That(valueToTest_bool, Is.Not.EqualTo(false));
    Assert.That(valueToTest_string, Is.Not.EqualTo("some other result"));
    Assert.That(valueToTest_datetime, Is.Not.EqualTo(new DateTime(2019, 12, 01)));
    Assert.That(valueToTest_obj, Is.Not.EqualTo(expectedValue_obj_notequal));

}
Sample Output:
Expected: <{ Foo=zoom, Baz=False }>
But was:  <{ Foo=bar, Baz=True }>


Expected: not equal to 2019-01-01 00:00:00
But was:  2019-01-01 00:00:00


Expected string length 17 but was 11. Strings differ at index 5.
Expected: "some other result"
But was:  "some result"
----------------^
Note that the string comparison has an "arrow" showing where the different occurred.
/// <summary>
/// Assertions that test for equality using object.Equals() to compare the actual value
/// to the expected value
/// </summary>
[Test]
public void EqualityChecks()
{
    bool valueToTest_bool = true;
    string valueToTest_string = "some result";
    DateTime valueToTest_datetime = new DateTime(2019, 01, 01);

    var valueToTest_obj = new { Foo = "bar", Baz = true };
    var expectedValue_obj_equal = new { Foo = "bar", Baz = true };
    var expectedValue_obj_notequal = new { Foo = "zoom", Baz = false }; ;

    // (important: expected value comes first!)
    Assert.AreEqual(true, valueToTest_bool);
    Assert.AreEqual("some result", valueToTest_string);
    Assert.AreEqual(new DateTime(2019, 01, 01), valueToTest_datetime);
    Assert.AreEqual(expectedValue_obj_equal, valueToTest_obj);

    Assert.AreNotEqual(false, valueToTest_bool);
    Assert.AreNotEqual("some other result", valueToTest_string);
    Assert.AreNotEqual(new DateTime(2019, 12, 01), valueToTest_datetime);
    Assert.AreNotEqual(expectedValue_obj_notequal, valueToTest_obj);
}
Sample Output:
Expected: <{ Foo=zoom, Baz=False }>
But was:  <{ Foo=bar, Baz=True }>

Expected: not equal to 2019-01-01 00:00:00
But was:  2019-01-01 00:00:00


Expected string length 17 but was 11. Strings differ at index 5.
Expected: "some other result"
But was:  "some result"
----------------^
Note that the string comparison has an "arrow" showing where the different occurred.
/// <summary>
/// Assertions that test for equality using object.Equals() to compare the actual value
/// to the expected value
/// </summary>
[Fact]
public void EqualityChecks()
{
    string valueToTest_string = "some result";
    DateTime valueToTest_datetime = new DateTime(2019, 01, 01);

    var valueToTest_obj = new { Foo = "bar", Baz = true };
    var expectedValue_obj_equal = new { Foo = "bar", Baz = true };
    var expectedValue_obj_notequal = new { Foo = "zoom", Baz = false }; ;


    // (important: expected value comes first!)
    Assert.Equal("some result", valueToTest_string);
    Assert.Equal(new DateTime(2019, 01, 01), valueToTest_datetime);
    Assert.Equal(expectedValue_obj_equal, valueToTest_obj);

    Assert.NotEqual("some other result", valueToTest_string);
    Assert.NotEqual(new DateTime(2019, 12, 01), valueToTest_datetime);
    Assert.NotEqual(expectedValue_obj_notequal, valueToTest_obj);
}
Sample Output:
Xunit.Sdk.EqualException: Assert.Equal() Failure
Expected: { Foo = zoom, Baz = False }
Actual:   { Foo = bar, Baz = True }


Xunit.Sdk.NotEqualException: Assert.NotEqual() Failure
Expected: Not 2019-01-01T00:00:00.0000000
Actual:   2019-01-01T00:00:00.0000000


Xunit.Sdk.EqualException: Assert.Equal() Failure
           ↓ (pos 5)
Expected: some other result
Actual:   some result
           ↑ (pos 5)
Note that the string comparison has an "arrow" showing where the different occurred.
/// <summary>
/// Assertions that test for equality using object.Equals() to compare the actual value
/// to the expected value
/// </summary>
[TestMethod]
public void EqualityChecks()
{

    bool valueToTest_bool = true;
    string valueToTest_string = "some result";
    DateTime valueToTest_datetime = new DateTime(2019, 01, 01);

    var valueToTest_obj = new { Foo = "bar", Baz = true };
    var expectedValue_obj_equal = new { Foo = "bar", Baz = true };
    var expectedValue_obj_notequal = new { Foo = "zoom", Baz = false }; ;


    //    (important: expected value comes first!)
    Assert.AreEqual(true, valueToTest_bool);
    Assert.AreEqual("some result", valueToTest_string);
    Assert.AreEqual(new DateTime(2019, 01, 01), valueToTest_datetime);
    Assert.AreEqual(expectedValue_obj_equal, valueToTest_obj);

    Assert.AreNotEqual(false, valueToTest_bool);
    Assert.AreNotEqual("some other result", valueToTest_string);
    Assert.AreNotEqual(new DateTime(2019, 12, 01), valueToTest_datetime);
    Assert.AreNotEqual(expectedValue_obj_notequal, valueToTest_obj);
}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.AreEqual failed. Expected:<{ Foo=zoom, Baz=False }>. Actual:<{ Foo=bar, Baz=True }>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.AreNotEqual failed. Expected any value except:<1/1/2019 12:00:00 AM>. Actual:<1/1/2019 12:00:00 AM>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.AreEqual failed. Expected:<some other result>. Actual:<some result>.

This second set of equality checks are utilizing object.ReferenceEquals() to determine if the two objects are actually referring to the same exact object in memory

/// <summary>
/// Assertions that test for equality using object.ReferenceEquals() to determine if both values point to
/// the exact same object.
/// </summary>
[Test]
public void SameObjectChecks()
{
    var valueToTest = new {Foo = "bar", Baz = true};
    var expectedValue_same = valueToTest;
    var expectedValue_notsame = new { Foo = "bar", Baz = true }; ;

    Assert.That(valueToTest, Is.SameAs(expectedValue_same));
    Assert.That(valueToTest, Is.Not.SameAs(expectedValue_notsame));

}
Sample Output:
Expected: same as <{ Foo=bar, Baz=True }>
But was:  <{ Foo=bar, Baz=True }>


Expected: not same as <{ Foo=bar, Baz=True }>
But was:  <{ Foo=bar, Baz=True }>
/// <summary>
/// Assertions that test for equality using object.ReferenceEquals() to determine if both values point to
/// the exact same object.
/// </summary>
[Test]
public void SameObjectChecks()
{
    var valueToTest = new {Foo = "bar", Baz = true};
    var expectedValue_same = valueToTest;
    var expectedValue_notsame = new { Foo = "bar", Baz = true }; ;

    // (important: expected value comes first!)
    Assert.AreSame(expectedValue_same, valueToTest);
    Assert.AreNotSame(expectedValue_notsame, valueToTest);
}
Sample Output:
Expected: same as <{ Foo=bar, Baz=True }>
But was:  <{ Foo=bar, Baz=True }>


Expected: not same as <{ Foo=bar, Baz=True }>
But was:  <{ Foo=bar, Baz=True }>
/// <summary>
/// Assertions that test for equality using object.ReferenceEquals() to determine if both values point to
/// the exact same object.
/// </summary>
[Fact]
public void SameObjectChecks()
{
    var valueToTest = new { Foo = "bar", Baz = true };
    var expectedValue_same = valueToTest;
    var expectedValue_notsame = new { Foo = "bar", Baz = true }; ;


    // (important: expected value comes first!)
    Assert.Same(expectedValue_same, valueToTest);
    Assert.NotSame(expectedValue_notsame, valueToTest);
}
Sample Output:
Xunit.Sdk.SameException: Assert.Same() Failure
Expected: { Foo = bar, Baz = True }
Actual:   { Foo = bar, Baz = True }


Xunit.Sdk.NotSameException: Assert.NotSame() Failure
/// <summary>
/// Assertions that test for equality using object.ReferenceEquals() to determine if both values point to
/// the exact same object.
/// </summary>
[TestMethod]
public void SameObjectChecks()
{
    var valueToTest = new { Foo = "bar", Baz = true };
    var expectedValue_same = valueToTest;
    var expectedValue_notsame = new { Foo = "bar", Baz = true }; ;


    // (important: expected value comes first!)
    Assert.AreSame(expectedValue_same, valueToTest);
    Assert.AreNotSame(expectedValue_notsame, valueToTest);

}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.AreSame failed.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.AreNotSame failed.

Comparision Checks

This first set will check your value against null

/// <summary>
/// Assertions that test for null values
/// </summary>
[Test]
public void NullChecks()
{
    var valueToTest = new { Foo = (object) null, Baz = new object() };

    Assert.That(valueToTest.Foo, Is.Null);
    Assert.That(valueToTest.Baz, Is.Not.Null);

}
Sample Output:
Expected: not null
But was:  null

Expected: null
But was:  <System.Object>
/// <summary>
/// Assertions that test for null values
/// </summary>
[Test]
public void NullChecks()
{
    var valueToTest = new { Foo = (object) null, Baz = new object() };

    Assert.Null(valueToTest.Foo);
    Assert.IsNull(valueToTest.Foo);
    Assert.NotNull(valueToTest.Baz);
    Assert.IsNotNull(valueToTest.Baz);
}
Sample Output:
Expected: not null
But was:  null

Expected: null
But was:  <System.Object>
/// <summary>
/// Assertions that test for null values
/// </summary>
[Fact]
public void NullChecks()
{
    var valueToTest = new { Foo = (object)null, Baz = new object() };

    Assert.Null(valueToTest.Foo);
    Assert.NotNull(valueToTest.Baz);

}
Sample Output:
Xunit.Sdk.NotNullException: Assert.NotNull() Failure

Xunit.Sdk.NullException: Assert.Null() Failure
Expected: (null)
Actual:   Object { }
/// <summary>
/// Assertions that test for null values
/// </summary>
[TestMethod]
public void NullChecks()
{
    var valueToTest = new {Foo = (object) null, Baz = new object()};

    Assert.IsNull(valueToTest.Foo);
    Assert.IsNotNull(valueToTest.Baz);
}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsNotNull failed.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsNull failed.

This next set will compare values relative to each other (greater than, less than, etc).

A benefit of using NUnit

This is an area where I think NUnit really outshines the other frameworks. With the other frameworks, you're mostly limited to Assert.IsTrue(some_condition), which only gives you a pass/fail result. With NUnit's Constraint model, you get a much richer syntax that provides significantly more detail in the built-in failure message.

Note that you can take it even further with NUnit and not just compare two values, but provide a margin of error using the .Within() helpers. This is particularly helpful when comparing floating-point numbers where the actual value may suffer from slight variations from your expected value.

/// <summary>
/// Assertions that compare the value to a set of constraints
/// </summary>
[Test]
public void ComparisonChecks()
{
    int bigNumber = int.MaxValue;
    int smallNumber = int.MinValue;
    int zero = 0;

    double notANumber = double.NaN; // NaN == 0D / 0D
    double infinity = double.PositiveInfinity;

    bool trueValue = true;
    bool falseValue = false;

    DateTime jan1 = new DateTime(2019, 01, 01);



    Assert.That(bigNumber, Is.GreaterThan(smallNumber));
    Assert.That(bigNumber, Is.GreaterThanOrEqualTo(smallNumber));

    Assert.That(smallNumber, Is.LessThan(bigNumber));
    Assert.That(smallNumber, Is.LessThanOrEqualTo(bigNumber));

    Assert.That(trueValue, Is.True);
    Assert.That(falseValue, Is.False);

    Assert.That(bigNumber, Is.Positive);
    Assert.That(bigNumber, Is.Not.Negative);
    Assert.That(smallNumber, Is.Negative);
    Assert.That(smallNumber, Is.Not.Positive);

    Assert.That(zero, Is.Zero);
    Assert.That(bigNumber, Is.Not.Zero);

    Assert.That(notANumber, Is.NaN);
    Assert.That(infinity, Is.Not.NaN);

    Assert.That(zero, Is.InRange(-100, 5));
    Assert.That(zero, Is.Not.InRange(1, 10));
    Assert.That(jan1, Is.InRange(new DateTime(2018, 01, 01), new DateTime(2019, 12, 31)));

    Assert.That(zero, Is.AnyOf(42, 0, 100));

    Assert.That(2.333333d, Is.EqualTo(2.3).Within(0.5));
    Assert.That(jan1, Is.EqualTo(new DateTime(2019, 01, 10)).Within(10).Days);
    Assert.That(95, Is.EqualTo(100).Within(8).Percent);


}
Sample Output:
Expected: not greater than or equal to -2147483648
But was:  2147483647

Expected: less than 2147483647
But was:  2147483647

Expected: False
But was:  True

Expected: 0
But was:  2147483647

Expected: NaN
But was:  ∞

Expected: in range (1,10)
But was:  0

Expected: not in range (1/1/2018 12:00:00 AM,12/31/2019 12:00:00 AM)
But was:  2019-01-01 00:00:00

Expected: any of < 42, 0, 100 >
But was:  33

Expected: 5.2999999999999998d +/- 0.5d
But was:  2.3333330000000001d

Expected: not equal to 2019-01-10 00:00:00 +/- 10.00:00:00
But was:  2019-01-01 00:00:00

Expected: 100 +/- 8 Percent
But was:  76
/// <summary>
/// Assertions that compare the value to a set of constraints
/// </summary>
[Test]
public void ComparisonChecks()
{
    int bigNumber = int.MaxValue;
    int smallNumber = int.MinValue;
    int zero = 0;

    double notANumber = double.NaN; // NaN == 0D / 0D
    double infinity = double.PositiveInfinity;

    bool trueValue = true;
    bool falseValue = false;


    Assert.Greater(bigNumber, smallNumber);
    Assert.GreaterOrEqual(bigNumber, smallNumber);

    Assert.Less(smallNumber, bigNumber);
    Assert.LessOrEqual(smallNumber, bigNumber);

    Assert.True(trueValue);
    Assert.False(falseValue);

    Assert.Positive(bigNumber);
    Assert.Negative(smallNumber);

    Assert.Zero(zero);
    Assert.NotZero(bigNumber);

    Assert.IsNaN(notANumber);

}
Sample Output:
Expected: greater than or equal to 2147483647
But was:  -2147483648

Expected: less than -2147483648
But was:  2147483647

Expected: False
But was:  True

Expected: 0
But was:  2147483647

Expected: NaN
But was:  ∞
/// <summary>
/// Assertions that compare the value to a set of constraints
/// </summary>
[Fact]
public void ComparisonChecks()
{
    int bigNumber = int.MaxValue;
    int smallNumber = int.MinValue;
    int zero = 0;

    double notANumber = double.NaN; // NaN == 0D / 0D
    double infinity = double.PositiveInfinity;


    bool trueValue = true;
    bool falseValue = false;

    DateTime jan1 = new DateTime(2019, 01, 01);

    // Constraint-style asserts:
    Assert.True(bigNumber  > smallNumber);
    Assert.True(bigNumber >= smallNumber);

    Assert.True(smallNumber < bigNumber);
    Assert.True(smallNumber <= bigNumber);

    Assert.True(trueValue);
    Assert.False(falseValue);


    Assert.InRange(zero, -100, 5);
    Assert.NotInRange(zero, 1, 10);
    Assert.InRange(jan1, new DateTime(2018, 01, 01), new DateTime(2019, 12, 31));

}
Sample Output:
Xunit.Sdk.TrueException: Assert.True() Failure
Expected: True
Actual:   False

Xunit.Sdk.InRangeException: Assert.InRange() Failure
Range:  (-100 - 5)
Actual: 23

Xunit.Sdk.InRangeException: Assert.InRange() Failure
Range:  (1/1/2018 12:00:00 AM - 12/31/2018 12:00:00 AM)
Actual: 1/1/2019 12:00:00 AM
/// <summary>
/// Assertions that compare the value to a set of constraints
/// </summary>
[TestMethod]
public void ComparisonChecks()
{
    int bigNumber = int.MaxValue;
    int smallNumber = int.MinValue;

    bool trueValue = true;
    bool falseValue = false;

    Assert.IsTrue(bigNumber > smallNumber);
    Assert.IsTrue(bigNumber >= smallNumber);

    Assert.IsTrue(smallNumber < bigNumber);
    Assert.IsTrue(smallNumber <= bigNumber);

    Assert.IsTrue(trueValue);
    Assert.IsFalse(falseValue);

}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsTrue failed.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsFalse failed.

String Checks

Verify the contents of a string, from simple patters (StartsWith, Contains) to more complicated Regular Expression matching.

/// <summary>
/// String-specific checks
/// </summary>
[Test]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";

    Assert.That("", Is.Empty);
    Assert.That(valueToTest, Is.Not.Empty);
    Assert.That(valueToTest, Does.Contain("Bar"));
    Assert.That(valueToTest, Does.Not.Contain("Bang"));
    Assert.That(valueToTest, Does.StartWith("Foo"));
    Assert.That(valueToTest, Does.Not.StartWith("Bar"));
    Assert.That(valueToTest, Does.EndWith("Bin"));
    Assert.That(valueToTest, Does.Not.EndWith("Baz"));
    Assert.That(valueToTest, Is.EqualTo("foo bar baz bin").IgnoreCase);
    Assert.That(valueToTest, Is.Not.EqualTo("something else").IgnoreCase);
    Assert.That(valueToTest, Does.Match("^Foo.*Bin$")); // param is a regex pattern
    Assert.That(valueToTest, Does.Not.Match("^Foo.*Bar$")); // param is a regex pattern

}
Sample Output:
Expected: <empty>
But was:  "asdf"


Expected: <empty>
But was:  "Foo Bar Baz Bin"

Expected: not String containing "Bar"
But was:  "Foo Bar Baz Bin"

Expected: String containing "Bang"
But was:  "Foo Bar Baz Bin"


Expected: not String starting with "Foo"
But was:  "Foo Bar Baz Bin"


Expected: String starting with "Bar"
But was:  "Foo Bar Baz Bin"


Expected: not String ending with "Bin"
But was:  "Foo Bar Baz Bin"


Expected: String ending with "Baz"
But was:  "Foo Bar Baz Bin"


Expected: not equal to "foo bar baz bin", ignoring case
But was:  "Foo Bar Baz Bin"


Expected string length 14 but was 15. Strings differ at index 0.
Expected: "something else", ignoring case
But was:  "Foo Bar Baz Bin"
-----------^


Expected: not String matching "^Foo.*Bin$"
But was:  "Foo Bar Baz Bin"


Expected: String matching "^Foo.*Bar$"
But was:  "Foo Bar Baz Bin"
/// <summary>
/// String-specific checks
/// </summary>
[Test]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";

    StringAssert.Contains("Bar", valueToTest);
    StringAssert.DoesNotContain("Bang", valueToTest);
    StringAssert.StartsWith("Foo", valueToTest);
    StringAssert.DoesNotStartWith("Bar", valueToTest);
    StringAssert.EndsWith("Bin", valueToTest);
    StringAssert.DoesNotEndWith("Baz", valueToTest);
    StringAssert.AreEqualIgnoringCase("foo bar baz bin", valueToTest);
    StringAssert.AreNotEqualIgnoringCase("something else", valueToTest);
    StringAssert.IsMatch("^Foo.*Bin$", valueToTest); //first param is a regex pattern
    StringAssert.DoesNotMatch("^Foo.*Bar$", valueToTest); //first param is a regex pattern
}
Sample Output:
Expected: not String containing "Bar"
But was:  "Foo Bar Baz Bin"


Expected: String containing "Bang"
But was:  "Foo Bar Baz Bin"

Expected: not String starting with "Foo"
But was:  "Foo Bar Baz Bin"


Expected: String starting with "Bar"
But was:  "Foo Bar Baz Bin"


Expected: not String ending with "Bin"
But was:  "Foo Bar Baz Bin"


Expected: String ending with "Baz"
But was:  "Foo Bar Baz Bin"


Expected: not equal to "foo bar baz bin", ignoring case
But was:  "Foo Bar Baz Bin"



Expected string length 14 but was 15. Strings differ at index 0.
Expected: "something else", ignoring case
But was:  "Foo Bar Baz Bin"
-----------^


Expected: not String matching "^Foo.*Bin$"
But was:  "Foo Bar Baz Bin"



Expected: String matching "^Foo.*Bar$"
But was:  "Foo Bar Baz Bin"
/// <summary>
/// String-specific checks
/// </summary>
[Fact]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";


    Assert.Contains("Bar", valueToTest);
    Assert.DoesNotContain("Bang", valueToTest);
    Assert.StartsWith("Foo", valueToTest);
    Assert.EndsWith("Bin", valueToTest);
    Assert.Equal("foo bar baz bin", valueToTest, ignoreCase: true);
    Assert.NotEqual("something else", valueToTest, StringComparer.InvariantCultureIgnoreCase);
    Assert.Matches("^Foo.*Bin$", valueToTest); //first param is a regex pattern
    Assert.Matches(new Regex("^Foo.*Bin$"), valueToTest);
    Assert.DoesNotMatch("^Foo.*Bar$", valueToTest); //first param is a regex pattern
    Assert.DoesNotMatch(new Regex("^Foo.*Bar$"), valueToTest);
}
Sample Output:
Xunit.Sdk.DoesNotContainException: Assert.DoesNotContain() Failure
Found:    Bar
In value: Foo Bar Baz Bin

Xunit.Sdk.ContainsException: Assert.Contains() Failure
Not found: Bang
In value:  Foo Bar Baz Bin


Xunit.Sdk.StartsWithException: Assert.StartsWith() Failure:
Expected: Bin
Actual:   Foo...

Xunit.Sdk.EndsWithException: Assert.EndsWith() Failure:
Expected:    Foo
Actual:   ···Bin

Xunit.Sdk.EqualException: Assert.Equal() Failure
      ↓ (pos 0)
Expected: something else
Actual:   Foo Bar Baz Bin
      ↑ (pos 0)

Xunit.Sdk.NotEqualException: Assert.NotEqual() Failure
Expected: Not "foo bar baz bin"
Actual:   "Foo Bar Baz Bin"

Xunit.Sdk.MatchesException: Assert.Matches() Failure:
Regex: ^Foo.*Bar$
Value: Foo Bar Baz Bin

Xunit.Sdk.DoesNotMatchException: Assert.DoesNotMatch() Failure:
Regex: ^Foo.*Bin
Value: Foo Bar Baz Bin
/// <summary>
/// String-specific checks
/// </summary>
[TestMethod]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";

    StringAssert.Contains(valueToTest, "Bar");
    StringAssert.StartsWith(valueToTest, "Foo");
    StringAssert.EndsWith(valueToTest, "Bin");
    StringAssert.Matches(valueToTest, new Regex( "^Foo.*Bin$"));
    StringAssert.DoesNotMatch(valueToTest, new Regex( "^Foo.*Bar$"));

}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: StringAssert.Contains failed. String 'Foo Bar Baz Bin' does not contain string 'asdf'. .

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: StringAssert.StartsWith failed. String 'Foo Bar Baz Bin' does not start with string 'Bar'. .

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: StringAssert.EndsWith failed. String 'Foo Bar Baz Bin' does not end with string 'Far'. .

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: StringAssert.Matches failed. String 'Foo Bar Baz Bin' does not match pattern '^Foo.*Bar$'. .

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: StringAssert.DoesNotMatch failed. String 'Foo Bar Baz Bin' matches pattern '^Foo.*Bin$'. .

Type Checks

Verify whether an object is a certain type, or could be used as a certain type.

/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[Test]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] { };


    Assert.That("foo", Is.AssignableFrom(typeof(string)));
    Assert.That("foo", Is.AssignableFrom<string>());

    Assert.That("foo", Is.Not.AssignableFrom(typeof(int)));
    Assert.That("foo", Is.Not.AssignableFrom<int>());


    Assert.That(stringList, Is.InstanceOf(typeof(List<string>)));
    Assert.That(stringList, Is.InstanceOf<List<string>>());

    Assert.That(intEnumerable, Is.Not.InstanceOf(typeof(List<int>)));
    Assert.That(intEnumerable, Is.Not.InstanceOf<List<int>>());

    Assert.That(stringList, Is.AssignableTo(typeof(IEnumerable<string>)));
    Assert.That(stringList, Is.AssignableTo<IEnumerable<string>>());

    Assert.That(stringList, Is.Not.AssignableTo(typeof(string[])));
    Assert.That(stringList, Is.Not.AssignableTo<string[]>());


    Assert.That(intEnumerable, Is.TypeOf(typeof(int[]))); //must be exact type
    Assert.That(intEnumerable, Is.TypeOf<int[]>()); //must be exact type


    Assert.That(stringList, Is.Not.TypeOf(typeof(IEnumerable<string>))); //must be exact type
    Assert.That(stringList, Is.Not.TypeOf<IEnumerable<string>>()); //must be exact type

}
Sample Output:
Expected: not assignable from <System.String>
But was:  <System.String>


Expected: assignable from <System.Int32>
But was:  <System.String>


Expected: not instance of <System.Collections.Generic.List`1[System.String]>
But was:  <System.Collections.Generic.List`1[System.String]>


Expected: instance of <System.Collections.Generic.List`1[System.Int32]>
But was:  <System.Int32[]>


Expected: not assignable to <System.Collections.Generic.IEnumerable`1[System.String]>
But was:  <System.Collections.Generic.List`1[System.String]>


Expected: assignable to <System.String[]>
But was:  <System.Collections.Generic.List`1[System.String]>


Expected: not <System.Int32[]>
But was:  <System.Int32[]>


Expected: <System.Collections.Generic.IEnumerable`1[System.String]>
But was:  <System.Collections.Generic.List`1[System.String]>
/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[Test]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] { };


    Assert.IsAssignableFrom(typeof(string), "foo");
    Assert.IsAssignableFrom<string>("foo");

    Assert.IsNotAssignableFrom(typeof(int), "foo");
    Assert.IsNotAssignableFrom<int>("foo");

    Assert.IsInstanceOf(typeof(List<string>), stringList);
    Assert.IsInstanceOf<List<string>>(stringList);

    Assert.IsNotInstanceOf(typeof(List<int>), intEnumerable);
    Assert.IsNotInstanceOf<List<int>>(intEnumerable);
}
Sample Output:
Expected: not assignable from <System.String>
But was:  <System.String>

Expected: assignable from <System.Int32>
But was:  <System.String>

Expected: not instance of <System.Collections.Generic.List`1[System.String]>
But was:  <System.Collections.Generic.List`1[System.String]>

Expected: instance of <System.Collections.Generic.List`1[System.Int32]>
But was:  <System.Int32[]>
/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[Fact]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] { };

    Assert.IsAssignableFrom<string>("foo");
    Assert.IsType<List<string>>(stringList);
    Assert.IsNotType<List<int>>(intEnumerable);
}
Sample Output:
Xunit.Sdk.IsAssignableFromException: Assert.IsAssignableFrom() Failure
Expected: typeof(int)
Actual:   typeof(string)

Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Actual:   System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

Xunit.Sdk.IsNotTypeException: Assert.IsNotType() Failure
Expected: typeof(System.Collections.Generic.List<string>)
Actual:   typeof(System.Collections.Generic.List<string>)
/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[TestMethod]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] { };

    Assert.IsInstanceOfType(stringList, typeof(List<string>));
    Assert.IsNotInstanceOfType(intEnumerable, typeof(List<int>));

}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsNotInstanceOfType failed. Wrong Type:<System.Collections.Generic.List`1[System.String]>. Actual type:<System.Collections.Generic.List`1[System.String]>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.IsInstanceOfType failed.  Expected type:<System.Collections.Generic.List`1[System.Int32]>. Actual type:<System.Int32[]>.

Collection Checks

Verify the contents of a collection meet some expectations.

/// <summary>
/// Checks specific to collections
/// </summary>
[Test]
public void CollectionChecks()
{
    var objArr = new object[] {new object(), 42, "my string"};
    var stringArr = new object[] {"foo", "bar", "baz", "bin", ""};
    var intList = Enumerable.Range(0, 100);


    Assert.That(stringArr, Is.All.TypeOf<string>());
    Assert.That(intList, Is.All.GreaterThanOrEqualTo(0));
    Assert.That(objArr, Is.All.Not.Null);

    Assert.That(intList, Is.Unique);


    Assert.That(intList, Is.EqualTo(Enumerable.Range(0, 100)));
    Assert.That(intList, Is.Not.EqualTo(Enumerable.Range(1, 5)));

    Assert.That(stringArr, Is.EquivalentTo(new string[] { "bar", "baz", "", "bin", "foo" }));
    Assert.That(stringArr, Is.Not.EquivalentTo(new string[] { "bar", "baz" }));


    Assert.That(stringArr, Has.Member("foo"));
    Assert.That(stringArr, Does.Contain("foo"));
    Assert.That(stringArr, Contains.Item("foo"));

    Assert.That(stringArr, Has.No.Member("zoom"));
    Assert.That(stringArr, Does.Not.Contain("zoom"));

    Assert.That(Enumerable.Range(5, 20), Is.SubsetOf(intList));
    Assert.That(Enumerable.Range(-1, 1), Is.Not.SubsetOf(intList));


    Assert.That(intList, Is.SupersetOf(Enumerable.Range(5, 20)));
    Assert.That(intList, Is.Not.SupersetOf(Enumerable.Range(-1, 1)));

    Assert.That(new int[] { }, Is.Empty);
    Assert.That(intList, Is.Not.Empty);

    Assert.That(new int[] { 1, 2, 3 }, Is.Ordered);
    Assert.That(new int[] { 2, 1, 3 }, Is.Not.Ordered);

    string[] sarray = new string[] { "a", "aa", "aaa" };
    Assert.That(sarray, Is.Ordered.By("Length"));
    Assert.That(sarray, Is.Ordered.Ascending.By("Length"));

    Assert.That(intList, Has.Exactly(100).Items);
    Assert.That(intList, Has.Exactly(50).Items.GreaterThanOrEqualTo(50));

    Assert.That(intList, Has.None.LessThan(0));
    Assert.That(objArr, Has.Some.TypeOf<string>());
    Assert.That(intList, Has.All.GreaterThanOrEqualTo(0));

}
Sample Output:
Expected: all items <System.Int32>
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >
First non-matching item at index [0]:  "foo"

Expected: all items greater than or equal to 3
But was:  < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >
First non-matching item at index [0]:  0


Expected: all items unique
But was:  < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >

Expected: equivalent to < "bar", "baz", <string.Empty>, "bin", "foo", "extra" >
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >
Missing (1): < "extra" >

Expected: some item equal to "extra"
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >

Expected: subset of < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >
But was:  < -5, -4, -3, -2, -1, 0, 1, 2, 3, 4... >

Expected: superset of < -5, -4, -3, -2, -1, 0, 1, 2, 3, 4... >
But was:  < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >

Expected: <empty>
But was:  < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >

Expected: collection ordered
But was:  < 2, 1, 3 >
Ordering breaks at index [1]:  1

Expected: collection ordered by "Length"
But was:  < "aa", "a", "aaa" >
Ordering breaks at index [1]:  "a"

Expected: exactly 50 items greater than or equal to 51
But was:  49 items < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >
/// <summary>
/// Checks specific to collections
/// </summary>
[Test]
public void CollectionChecks()
{
    var objArr = new object[] {new object(), 42, "my string"};
    var stringArr = new object[] {"foo", "bar", "baz", "bin", ""};


    var intList = Enumerable.Range(0, 100);

    CollectionAssert.AllItemsAreInstancesOfType(stringArr, typeof(string));
    CollectionAssert.AllItemsAreNotNull(objArr);

    CollectionAssert.AllItemsAreUnique(intList);

    CollectionAssert.AreEqual(Enumerable.Range(0, 100), intList);
    CollectionAssert.AreNotEqual(Enumerable.Range(1, 5), intList);

    CollectionAssert.AreEquivalent(new string[] { "bar", "baz", "", "bin", "foo" }, stringArr);
    CollectionAssert.AreNotEquivalent(new string[] { "bar", "baz" }, stringArr);

    CollectionAssert.Contains(stringArr, "foo");
    CollectionAssert.DoesNotContain(stringArr, "zoom");

    CollectionAssert.IsSubsetOf(Enumerable.Range(5, 20), intList);
    CollectionAssert.IsNotSubsetOf(Enumerable.Range(-1, 1), intList);

    CollectionAssert.IsEmpty(new int[] { });
    CollectionAssert.IsNotEmpty(new int[] { 1, 2 });

    CollectionAssert.IsOrdered(new int[] { 1, 2, 3 });

}
Sample Output:
Expected: all items unique
But was:  < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >


Expected and actual are both <System.Linq.Enumerable+<RangeIterator>d__113>
Values differ at index [100]

Expected: equivalent to < "bar", "baz", <string.Empty>, "bin", "foo", "extra" >
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >
Missing (1): < "extra" >


Expected: some item equal to "extra"
But was:  < "foo", "bar", "baz", "bin", <string.Empty> >

Expected: subset of < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... >
But was:  < -5, -4, -3, -2, -1, 0, 1, 2, 3, 4... >


Expected: <empty>
But was:  < 1, 2 >

Expected: collection ordered
But was:  < 2, 1, 3 >
Ordering breaks at index [1]:  1
/// <summary>
/// Checks specific to collections
/// </summary>
[Fact]
public void CollectionChecks()
{
    var objArr = new object[] { new object(), 42, "my string" };
    var stringArr = new string[] { "foo", "bar", "baz", "bin", "" };
    var intList = Enumerable.Range(0, 100);


    Assert.All(stringArr, s => Assert.IsType<string>(s));
    Assert.All(objArr, Assert.NotNull );

    Assert.Equal(Enumerable.Range(0, 100), intList);
    Assert.NotEqual(Enumerable.Range(1, 5), intList);


    Assert.Contains("foo", stringArr);
    Assert.DoesNotContain("zoom", stringArr);

    Assert.Subset(intList.ToHashSet(), Enumerable.Range(5, 20).ToHashSet());
    Assert.Superset(Enumerable.Range(5, 20).ToHashSet(), intList.ToHashSet());

    Assert.Empty(new int[] { });
    Assert.NotEmpty(new int[] { 1, 2 });


}
Sample Output:
Xunit.Sdk.AllException: Assert.All() Failure: 5 out of 5 items in the collection did not pass.
[4]: Item:
 Xunit.Sdk.IsTypeException: Assert.IsType() Failure
 Expected: System.Int32
 Actual:   System.String
   ...
[3]: Item: bin
 Xunit.Sdk.IsTypeException: Assert.IsType() Failure
 Expected: System.Int32
 Actual:   System.String
    ...
[2]: Item: baz
 Xunit.Sdk.IsTypeException: Assert.IsType() Failure
 Expected: System.Int32
 Actual:   System.String
    ...
[1]: Item: bar
 Xunit.Sdk.IsTypeException: Assert.IsType() Failure
 Expected: System.Int32
 Actual:   System.String
    ...
[0]: Item: foo
 Xunit.Sdk.IsTypeException: Assert.IsType() Failure
 Expected: System.Int32
 Actual:   System.String


Xunit.Sdk.EqualException: Assert.Equal() Failure
Expected: <RangeIterator>d__113 [1, 2, 3, 4, 5, ...]
Actual:   <RangeIterator>d__113 [0, 1, 2, 3, 4, ...]


Xunit.Sdk.ContainsException: Assert.Contains() Failure
Not found: extra
In value:  String[] ["foo", "bar", "baz", "bin", ""]

Xunit.Sdk.SubsetException: Assert.Subset() Failure
Expected: HashSet<Int32> [0, 1, 2, 3, 4, ...]
Actual:   HashSet<Int32> [-5, -4, -3, -2, -1, ...]

Xunit.Sdk.SupersetException: Assert.Superset() Failure
Expected: HashSet<Int32> [-5, -4, -3, -2, -1, ...]
Actual:   HashSet<Int32> [0, 1, 2, 3, 4, ...]

Xunit.Sdk.EmptyException: Assert.Empty() Failure
Collection: [1, 2]
/// <summary>
/// Checks specific to collections
/// </summary>
[TestMethod]
public void CollectionChecks()
{
    var objArr = new object[] { new object(), 42, "my string" };
    var stringArr = new object[] { "foo", "bar", "baz", "bin", "" };
    var intList = Enumerable.Range(0, 100).ToList();

    CollectionAssert.AllItemsAreInstancesOfType(stringArr, typeof(string));
    CollectionAssert.AllItemsAreNotNull(objArr);

    CollectionAssert.AllItemsAreUnique(intList);

    CollectionAssert.AreEqual(Enumerable.Range(0, 100).ToList(), intList);
    CollectionAssert.AreNotEqual(Enumerable.Range(1, 5).ToList(), intList);

    CollectionAssert.AreEquivalent(new string[] { "bar", "baz", "", "bin", "foo" }, stringArr);
    CollectionAssert.AreNotEquivalent(new string[] { "bar", "baz" }, stringArr);

    CollectionAssert.Contains(stringArr, "foo");
    CollectionAssert.DoesNotContain(stringArr, "zoom");

    CollectionAssert.IsSubsetOf(Enumerable.Range(5, 20).ToList(), intList);
    CollectionAssert.IsNotSubsetOf(Enumerable.Range(-1, 1).ToList(), intList);

}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.AllItemsAreInstancesOfType failed. Element at index 0 is not of expected type. Expected type:<System.Int32>. Actual type:<System.String>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.AllItemsAreUnique failed. Duplicate item found:<10>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.AreEqual failed. (Different number of elements.)

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.AreEquivalent failed. The number of elements in the collections do not match. Expected:<6>. Actual:<5>.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.Contains failed.

Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: CollectionAssert.IsSubsetOf failed.

Exception Checks

If your code throws an exception, then it automatically fails the test (that's how Asserts work afterall). But sometimes you want to be deterministic in your tests. So these Exception Asserts allow you to do more detailed validation of the Exception you expect to be thrown.

/// <summary>
/// Exception-specific checks
/// </summary>
[Test]
public void ExceptionChecks()
{

    void MethodThatThrows() { throw new ArgumentException(); }

    Assert.That(() => { return; }, Throws.Nothing);


    Assert.That(MethodThatThrows, Throws.ArgumentException);
    Assert.That(MethodThatThrows, Throws.TypeOf<ArgumentException>());

    Assert.That(() => throw new Exception("message"),
        Throws.InstanceOf<Exception>()
            .And.With.Property(nameof(Exception.Message)).EqualTo("message"));


    // Require an ApplicationException - derived types fail!
    Assert.That(() => throw new ApplicationException("message"),
        Throws.TypeOf<ApplicationException>());

    // Allow both ApplicationException and any derived type
    Assert.That(() => throw new ApplicationException("message"),
        Throws.InstanceOf<Exception>());



}
Sample Output:
Expected: No Exception to be thrown
But was:  <System.NotImplementedException: opps!

Expected: <System.ArgumentException>
But was:  <System.NotImplementedException: opps!

Expected: instance of <System.Exception> and property Message equal to "message"
But was:  "opps!"

Expected: instance of <System.NotImplementedException>
But was:  <System.ApplicationException: message
/// <summary>
/// Exception-specific checks
/// </summary>
[Test]
public void ExceptionChecks()
{

    void MethodThatThrows() { throw new ArgumentException(); }


    Assert.DoesNotThrow(() => { return; });


    Assert.Throws<ArgumentException>(MethodThatThrows);
    Assert.Throws<ArgumentException>( () => throw new ArgumentException());

    Exception ex = Assert.Throws<Exception>(() => throw new Exception("message"));
    Assert.That(ex.Message, Is.EqualTo("message"));

    Assert.Throws(Is.TypeOf<Exception>().And.Message.EqualTo("message"),
        () => throw new Exception("message"));

    // Require an ApplicationException - derived types fail!
    Assert.Throws<ApplicationException>(() => throw new ApplicationException("message"));

    // Allow both ApplicationException and any derived type
    Assert.Throws(Is.InstanceOf<Exception>(), () => throw new ApplicationException("message"));


}
Sample Output:
Expected: No Exception to be thrown
But was:  <System.NotImplementedException: opps!

Expected: <System.ArgumentException>
But was:  <System.NotImplementedException: opps!

Expected: <System.Exception>
But was:  <System.NotImplementedException: message

Expected: <System.NotImplementedException> and property Message equal to "message"
But was:  <System.Exception: opps!

Expected: instance of <System.NotImplementedException>
But was:  <System.ApplicationException: message
/// <summary>
/// Exception-specific checks
/// </summary>
[Fact]
public void ExceptionChecks()
{
    void MethodThatThrows() { throw new ArgumentException(); }


    Assert.Throws<ArgumentException>(() => MethodThatThrows());

    Exception ex = Assert.Throws<Exception>((Action)(() => throw new Exception("message")));
    Assert.Equal("message", ex.Message);
}
Sample Output:
Xunit.Sdk.ThrowsException: Assert.Throws() Failure
Expected: typeof(System.ArgumentException)
Actual:   typeof(System.NotImplementedException): opps!
/// <summary>
/// Exception-specific checks
/// </summary>
[TestMethod]
public void ExceptionChecks()
{

    void MethodThatThrows() { throw new ArgumentException(); }


    Assert.ThrowsException<ArgumentException>(() => MethodThatThrows());
    Assert.ThrowsException<ArgumentException>(() => throw new ArgumentException());

    Exception ex = Assert.ThrowsException<Exception>(() => throw new Exception("message"));
    Assert.AreEqual("message", ex.Message);
}
Sample Output:
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.ThrowsException failed. Threw exception NotImplementedException, but exception ArgumentException was expected.
Exception Message: opps!

Dynamically Set Test Results

This set of Asserts allow you to directly set the staus of a test as Pass, Fail, Ignored or Inconclusive (which isn't supported in most runners)

/// <summary>
/// Assert calls that dynamically change the test results
/// </summary>
[Test]
public void ForcedResults()
{
    Assert.Pass("immediately end the test with a passing result");
    Assert.Fail("immediately end the test with a failure result");

    Assert.Ignore("dynamically cause a test or suite to be ignored at runtime");
    Assert.Inconclusive("indicates that the test could not be completed with the data available");
}
/// <summary>
/// Assert calls that dynamically change the test results
/// </summary>
[Fact]
public void ForcedResults()
{
    //There is no Assert.Fail option, but you can just thrown an exception.
    //Tip: Create your own Assert.Fail method to wrap throwing an exception
    throw new Exception("immediately end the test with a failure result");

    //There's not a built-in Assert.Skip (aka Ignore), but there is a somewhat complicated
    //way to do it. Example code here: https://github.com/xunit/samples.xunit/tree/master/DynamicSkipExample

}
/// <summary>
/// Assert calls that dynamically change the test results
/// </summary>
[TestMethod]
public void ForcedResults()
{
    Assert.Inconclusive("indicates that the test could not be completed with the data available");
    Assert.Fail("immediately end the test with a failure result");

}

NUnit Specialized Asserts

NUnit provides some additional Assert options that are useful in some specialized cases.

Multiple Asserts

Normally, once an Assert fails, execution of the test halts and no additional Asserts are attempted. NUnit provides Assert.Multiple that wraps your Assert calls and will attempt to run all your Assert statements and provide a consolidated failure message with all of the Asserts that failed.

/// <summary>
/// Syntax for executing multiple assertions in the same test (ie: all asserts are run)
/// </summary>
[Test]
public void MultipleCriteriaChecks()
{
    object aNumber = 5.0;

    Assert.Multiple(() =>
    {
        Assert.That(aNumber, Is.AssignableTo<double>());
        Assert.That(aNumber, Is.InRange(0.0, 10.0));
    }
    );
}
Sample Output:
Expected: assignable to <System.String>
But was:  <System.Double>

...

Expected: in range (0,10)
But was:  -5.0d

Additionally, the NUnit Constraint-style syntax allows you to chain assert conditions with .And. or .Or., so that a single Assert can match on multiple conditions at once.

/// <summary>
/// Syntax for executing multiple assertions in the same test (ie: all asserts are run)
/// </summary>
[Test]
public void MultipleCriteriaChecks()
{
    object aNumber = 5.0;

    Assert.That(aNumber, Is.AssignableTo<int>().Or.AssignableTo<double>());
    Assert.That(aNumber, Is.GreaterThanOrEqualTo(0).And.LessThanOrEqualTo(10));

}
Sample Output:
Expected: assignable to <System.Int32> or assignable to <System.Double>
But was:  "5.0"

Expected: greater than or equal to 0 and less than or equal to 10
But was:  -5.0d

Filesystem Asserts

NUnit also provides a set of File- and Directory-specific asserts for dealing with paths and file-related data.

/// <summary>
/// File- and Directory-specific checks
/// </summary>
[Test]
public void FileChecks()
{
    var realFilePath = Assembly.GetExecutingAssembly().Location;
    var realFileInfo = new FileInfo(realFilePath);

    var realDirectoryPath = Path.GetDirectoryName(realFilePath);
    var realDirectoryInfo = new DirectoryInfo(realDirectoryPath);

    var nonexistantFilePath = "E:/ fake.folder / this.is.fake";
    var nonexistantFileInfo = new FileInfo(nonexistantFilePath);

    var nonexistantDirectoryPath = Path.GetDirectoryName(nonexistantFilePath);
    var nonexistantDirectoryInfo = new DirectoryInfo(nonexistantDirectoryPath);



    Assert.That(realFilePath, Does.Exist);
    Assert.That(realFileInfo, Does.Exist);

    Assert.That(nonexistantFilePath, Does.Not.Exist);
    Assert.That(nonexistantFileInfo, Does.Not.Exist);

    Assert.That(realDirectoryPath, Does.Exist);
    Assert.That(realDirectoryInfo, Does.Exist);

    Assert.That(nonexistantDirectoryPath, Does.Not.Exist);
    Assert.That(nonexistantDirectoryInfo, Does.Not.Exist);

    Assert.That(realDirectoryInfo, Is.Not.Empty);

    Assert.That("/folder1/./junk/../folder2", Is.SamePath("/folder1/folder2"));
    Assert.That("/folder1/./junk/../folder2/..", Is.Not.SamePath("/folder1/folder2"));

    Assert.That("/folder1/./junk/../folder2", Is.SamePath("/FOLDER1/folder2").IgnoreCase);
    Assert.That("/folder1/./junk/../folder2", Is.Not.SamePath("/FOLDER1/folder2").RespectCase);


    Assert.That("/folder1/./junk/../folder2/./foo", Is.SamePathOrUnder("/folder1/folder2"));
    Assert.That("/folder1/./junk/../folder2/./foo", Is.SubPathOf("/folder1"));

}
Sample Output:
Expected: file or directory exists
But was:  "E:/ fake.folder / this.is.fake"

Expected: not file or directory exists
But was:  "C:\Users\wrigh\AppData\Local\NCrunch\15220\6\NUnit.FullFramework\bin\Debug\NUnit.FullFramework.dll"


Expected: not file or directory exists
But was:  "C:\Users\wrigh\AppData\Local\NCrunch\15220\6\NUnit.FullFramework\bin\Debug"

Expected: file or directory exists
But was:  "E:\fake.folder"


Expected: not Path matching "/folder1/folder2"
But was:  "/folder1/./junk/../folder2"

Expected: Path under or matching "/folder1/folder2/x"
But was:  "/folder1/./junk/../folder2/./foo"
/// <summary>
/// File- and Directory-specific checks
/// </summary>
[Test]
public void FileChecks()
{
    var realFilePath = Assembly.GetExecutingAssembly().Location;
    var realFileInfo = new FileInfo(realFilePath);

    var realDirectoryPath = Path.GetDirectoryName(realFilePath);
    var realDirectoryInfo = new DirectoryInfo(realDirectoryPath);

    var nonexistantFilePath = "E:/ fake.folder / this.is.fake";
    var nonexistantFileInfo = new FileInfo(nonexistantFilePath);

    var nonexistantDirectoryPath = Path.GetDirectoryName(nonexistantFilePath);
    var nonexistantDirectoryInfo = new DirectoryInfo(nonexistantDirectoryPath);


    // see: https://github.com/nunit/docs/wiki/File-Assert

    FileAssert.Exists(realFileInfo);
    FileAssert.Exists(realFilePath);

    FileAssert.DoesNotExist(nonexistantFileInfo);
    FileAssert.DoesNotExist(nonexistantFilePath);

    DirectoryAssert.Exists(realDirectoryPath);
    DirectoryAssert.Exists(realDirectoryInfo);

    DirectoryAssert.DoesNotExist(nonexistantDirectoryPath);
    DirectoryAssert.DoesNotExist(nonexistantDirectoryInfo);


}
Sample Output:
Expected: file exists
But was:  <E:/fake.folder/this.is.fake>

Expected: not file exists
But was:  <C:\Users\wrigh\AppData\Local\NCrunch\15220\3\NUnit.FullFramework\bin\Debug\NUnit.FullFramework.dll>


Expected: not directory exists
But was:  "C:\Users\wrigh\AppData\Local\NCrunch\15220\3\NUnit.FullFramework\bin\Debug"

Expected: directory exists
But was:  "E:\fake.folder"


As I've been giving my Mocking .NET Without Hurting It's Feelings talk, I've referenced several mocking frameworks, but only really gave code examples for a couple of them. In part, this was because more than that would make the presentation chaotic, but also because I had only a very limited exposer (if any) to some of the frameworks.

So I decided to put together a GitHub repository where I implemented the same basic tests using different mocking frameworks in order to a) provide side-by-side examples of how to use the frameworks and b) go a little deeper with some of the frameworks I haven't used much.

While I had no problems with the frameworks I've used (Moq, RhinoMocks, etc), when it came time to write real code using Microsoft Fakes, I quickly ran into a wall. A key feature of the frameworks I've used is the ability to verify a specific mock/stub was or was not called. Generally, this is done through some sort of AssertWasCalled() or AssertWasNotCalled() helper. But for Fakes, this just didn't exist. The functionality is there, but getting to it is like walking barefoot through fire.

fire walker

Seriously, in order to do something like AssertWasCalled, you need to first include a StubObserver when you create your stub, which will capture calls and other info:

var myMock = new StubIFoo {InstanceObserver = new StubObserver()};

Then, to determine if a method was called, you have to inspect that observer:

Assert.IsTrue(((StubObserver)myMock.InstanceObserver).GetCalls().Any(call => call.StubbedMethod.Name == "MyMethod"));

And if you want to check if specific arguments were passed in, then it get's worse:

 var myCall = ((StubObserver)myMock.InstanceObserver).GetCalls().First(call => call.StubbedMethod.Name == "MyMethod");
 object[] argsToMyCall = myCall.GetArguments().ToArray();

Now I've got an un-typed object array to walk through and check. Eek!

After a couple of hours of internet research and stale Stack Overflow posts, I landed on a NuGet package called Fakes.Contrib which did all the heavy lifting for you and allowed you to add .AsObservable() to your stub, then call .AssertWasCalled as you would expect. It even provided type placeholders so you could do With.Any<MyClass>() if you didn't care about a specific argument. This was exactly what I wanted (and what I would have expected Microsoft to have provided out-of-the-box).

// Arrange
var obj = new MyClass();
var stub = new StubIMyComponent().AsObservable();
var sut = MakeSut(stub);

// Act
sut.DoSomething(obj);

// Assert
stub.AssertWasCalled(mock => mock.MyMethod(With.Any<MyClass>()));

Now, I quickly ran into an issue: The Fakes.Contrib logic only worked on concreate classes. It didn't support using Interfaces, which my whole sample project was built with. But wait! It's Open Source! So I put together a pull request to add support for interfaces and reached out to the project owner Fabian Vilers to let him know it was coming. Fast forward a couple of weeks, and now I'm the primary owner of that project!

I'm not sure yet where this will go. I will add support here and there as I play with Microsoft Fakes, but since I don't have a project where I would use it extensively, I'm hoping to hear from people how do and get some feedback. If nothing else, it gives me some entry-level experience managing an open source project / nuget package.

You can checkout the Fakes.Contrib project on GitHub:

Or on NuGet.org: NuGet



Loading Visualizers (and Where To Install)

Visualizers registered in assemblies currently loaded into the application will be available. That is, the DebuggerVisualizer attribute must have been loaded and the assembly housing the visualizer must be loaded (otherwise you'll get an error message from Visual Studio saying it couldn't find the assembly).

If the visualizers are included in projects in the currently loaded solution, typically everything "just works". However, if the visualizers are in a seperate assembly (not part of the current solution) which you reference, you may need to force the assembly to be loaded. If you use the actual type in the DebuggerVisualizer attribute (ex: [DebuggerVisualizer(typeof(SomeTypeVisualizer))]), that should be enough. But if you use the string-based constructor for the attribute (ex: [DebuggerVisualizer("MyVisualizers.SomeTypeVisualizer")]), you may need to force the assembly to be loaded. One way to do that is add a static constructor to your main class that references a type from the visualizer assembly, like this:

class Program
{
    static Program()
    {
        if (Debugger.IsAttached)
        {
            //force loading of the visualizer assembly and Microsoft.VisualStudio.DebuggerVisualizers
            var foo = typeof(VisualizerExamples.DebuggerVisualizer.Exception.SimpleExceptionVisualizer);
        }
    }

This, however, makes it pretty difficult to include standalone visualizers in NuGet packages. (Though, including visualizers in the assembly with the types they visualize would work via NuGet)

The more common approach is to install the assembly with your visualizers into one of two places:

  • <VisualStudioInstallPath>\Common7\Packages\Debugger\Visualizers\ to make them available to all users on the computer.
  • My Documents\<VisualStudioVersion>\Visualizers\ to make them available to the current user.

Note: The visualizers must be installed on the machine running the application being debugged, so if you're using a remote debugging session, they must be installed on the remote machine as well as your local machine.

You can find a number of visualizers in the Visual Studio Marketplace, and they'll install into one of these locations.

Security Considerations

Keep in mind that parts of the Debugger Visualizer effectively run with the security level of Visual Studio, and all too many of us developers run Visual Studio as Administrator (thanks, in large part, to the crazy that is IIS's security model). So that means the visualizer you select could happily go off and delete your entire hard drive, send all of your source code to an FTP site, or whatever Hollywood-style hacker-in-a-hoodie nefarious stuff you can imagine.

So make sure you know and trust the source for any visualizers you choose to run. And since visualizers can been shipped with libraries you're using, they may be available without you having directly installed anything.

Visual Studio already has some safeguards in place, such as disallowing app-side VisualizerObjectSource object running in partial-trust. But, in my experience, very few of us make use of anything but full-trust environments. So Don't Be Stupid and pay a little extra attention to which visualizers you see vs. what you expect to see listed.

Supported Types and Limitations

Generics have very poor support. You can only register a visualizer with an open generic type (ex: Dictionary<,>) but not a constructed type (ex: Dictionary<int, string>). But in the visualizer, you only get an object, so without a lot of crazy reflection, you have no way of knowing the types used in the generic placeholders.

Universal Windows Platform (UWP) apps and Windows "Store" apps do not support custom visualizers, though they can use the built-in string visualizers (Text, HTML, XML, JSON).

The visualizers described in this post only work for managed code. Native VisualCpp assemblies won't use them.

Visual Studio for Mac uses the Mono Soft-Mode debugger, which does not use the same visualizers as Visual Studio for Windows.

Make sure to take a look at the other posts in my Debugger Visualizer series.



This post builds upon the first post in the series Writing A (ReadOnly) Custom Debugger Visualizer for Visual Studio. Please at least skim through that post to understand the topics in this one.

Making Your Visualizer Editable

There are cases where just viewing data from the object you're visualizing during a debugging session isn't enough. You'd like to modify the object in some way. Perhaps to setup a specific failure/success scenario or replicate a bug/bug fix.

If you're using a read-only visualizer, like what we've built so far in this series, you'd need to close the visualizer and use the Immediate Window to modify the data. Particularly if the value you want to modify is deeply nested in your object graph, or you have a bunch of items in a child collection and finding the right one to modify is tedious, then using the Immediate Window can be an exercise in frustration.

Luckily, the Debugger Visualizer API allows a great amount of flexibility to modify the object we're visualizing.

The Components Involved

Going from a read-only visualizer to one that can modify/interact with the underlying object means taking advantage of additional methods in the IVisualizerObjectProvider and VisualizerObjectSource.

Specially, in IVisualizerObjectProvider, you can call ReplaceData/ReplaceObject or TransferData/TransferObject to request changes to the live object. Just like the GetData/GetObject calls we discussed in earlier posts, the "Data" versions of the methods work against Stream objects and you must serialize/deserialize the values yourself, while the "Object" methods will call the "Data" method, then automatically serialize/deserialize for you.

Calls to IVisualizerObjectProvider.ReplaceData result in a call to VisualObjectSource.CreateReplacementObject on the app side. The intention of this API is to completely replace the live object with the version being passed into the ReplaceData call. If the object being visualized is Serializable, you can use the default implementation of VisualizerObjectSource.CreateReplacementObject, which will deserialize the incoming Stream and return the object as the new version. Otherwise, you'll need to write your own class that extends VisualizerObjectSource and overrides CreateReplacementObject to take in the Stream coming from the Visual Studio side (which could be a ViewModel object if the primary object isn't serializable) and convert it into an instance of the Type being visualized to replace the current object.

Additionally, you'll need to first verify that IVisualizerObjectProvider.IsObjectReplaceable is true. Otherwise, you won't be allowed to manipulate the object via the ReplaceData method. Documentation on when this would be false is scarce, but I did find this summary block for the property in a Patent filing for this feature:

public interface IVisualizerObjectProvider
{
    /// <summary>
    /// Specifies whether a replacement object can be created. For instance, if the object being
    /// visualized is a readonly field then this will return false.
    /// </summary>
    /// <returns></returns>
    bool IsObjectReplaceable { get; }

From https://www.google.com/patents/US7657873

Alternatively, you can use the TransferData method to send any arbitrary data stream from your visualizer to the VisualizerObjectSource.TransferData method. With this pair of methods, you can send serialized data from the Visual Studio side to the application side, and optionally have it reply back with a serialized object. This opens up the possibly of implementing a messaging system between the two side for more complex interactions.

For instance, in the case of a WebException, you could decide to use the actual WebException type in the GetData call, then, should the user click a button on the UI to review the response HTML, you could send a message to the application via the TransferData call, have it read the WebExceptionResponse.GetResponseStream() Stream and return the string version of the output.

NOTE: Communication is always initiated by the Visual Studio side -- specifically, calls from your Visualizer class into IVisualizerObjectProvider. The application-side cannot initiate the conversation and TransferData is the only method where it can return something other than the object being used by your visualizer.

The Logical Flow (For Modifying an Object from Your Visualizer)

The initial flow for getting data into your visualizer will remain the same (see the previous posts in this series). But once visualized, users can then interact with your UI to initiate changes to the object. Generally, this will be via OnChange-type events in your object/ViewModel or button click UI events that are handled by your Visualizer class. Thus, any changes or actions the user makes via the UI will be picked up by your Visualizer class.

Then, from your handler, you need to decide which API you want to use.

The simpler path would be to modify the version of the object you already have in memory (or have the UI bound directly to the object so it is automatically updated on changes), then call ReplaceObject with the updated object.

Otherwise, you can call TransferObject with some object of your choosing, such as a messaging DTO you've created that holds the action or changes you want to pass to the application.

This, in turn, will call the corresponding method in VisualizerObjectSource.

If you used ReplaceData or ReplaceObject, your implementation of CreateReplacementObject is called with the current object instance and the Stream of the serialized object coming from the IVisualizerObjectProvider. That method will need to take the incoming Stream and return an object. When the method returns the object, it will replace the original object in the applications memory.

If you used TransferData or TransferObject, your implementation of TransferData will be called. You cannot use the base implementation of TransferData -- it will just throw NotImplementedException). There are three parameters passed into VisualizerObjectSource.TransferData:

  • The original object being visualized
  • An incoming Stream for the data being passed from the visualizer TransferData
  • An outgoing Stream to allow a reply back to TransferData

The key for using TransferData is to have both sides of the call to have a common understanding of the types being passed around so that they can be deserialized/cast to the appropriate object Type rather than just object. Then it's just a matter of determining what data needs to be modified or what action needs to be taken and acting accordingly. Ultimately, the TransferData method needs to modify the passed-in original object if changes are needed.

In that case where TransferData is used, the third parameter passed into the VisualizerObjectSource.TransferData method is an outgoing Stream. This allows for a response to be returned to the Visualizer via the IVisualizerObjectProvider.TransferData's return value. For TransferData, the return value is a Stream which you must deserialize yourself, or if you used TransferObject, it's an object that you must cast to the appropriate Type. So again, having an common understanding of the types being transferred is key.

An example of how this might be used:

For a WebResponse visualizer, if you need the actual response value, you could call IVisualizerObjectProvider.TransferData with a message object requesting the actual response value. Then your VisualizerObjectSource.TransferData, upon decoding the message, would call Response.GetResponseStream() and read the response into a string object. Then, that string could be returned on the outgoing Stream. Then, the visualizer would receive the string as the return value of it's call to TransferData, and update the UI to display the string value.

Implementing a Read-Write Visualizer

Starting from the visualizer code at the end of my "Writing A (ReadOnly) Custom Debugger Visualizer for Visual Studio" post (which used a WinForms-based UI), I'll change a few things in the VisualizerObjectSource and Visualizer implementations.

The VisualizerObjectSource

TIP: If you use ReplaceData/ReplaceObject and you're passing the actual type being visualized (not a ViewModel), then you don't actually need to implement your own VisualizerObjectSource and/or override CreateReplacementObject, as the base implementation will just deserialize the passed-in object and replace the live object with the new version.

Otherwise, you'll override the CreateReplacementObject method to handle the conversion process. If you're passing a ViewModel into TransferData, then CreateReplacementObject will need to map the ViewModel's values into either a new instance of the type being visualized, or modify the original object (which is passed in as a parameter). Ultimately, the CreateReplacementObject call must return an object which will replace the live object being visualized in the application's memory.

WARNING: When using the ReplaceData calls, be aware of object pointers in your object and make sure you wire the pointers back up correctly. The serialization process may result in new objects being created as clones of your original objects and you'll need to use the CreateReplacementObject method to replace those with the original objects pointed to in your object graph.

    public override object CreateReplacementObject(object target, Stream incomingData)
    {
        var originalObject = target as SimpleExample.SomeType;

        var incomingChangedObject = Deserialize(incomingData) as SimpleExample.SomeType;
        // if this is a ViewModel, you'll need to map the value into either a new instance of 
        // the visualized type, or modify the originalObject with the ViewModel's values and return
        // the originalObject as the new instance.

        // Beware object pointers! If you have an object graph or other data types which will result in
        // cloned objects being created during the serialization/deserialization process, you'll need to
        // handle that here.

        //returns a instance of the object, which VS will substitute for the original object in memory
        return incomingChangedObject;           
    }

If you've used the TransferData/TransferObject, you must override the VisualizerObjectSource.TransferData method. As parameters, the method will receive the original object being visualized, the incoming Stream being sent from the IVisualizerObjectProvider, and an outbound Stream which can be used to send response messages/data back to the Visualizer.

    public override void TransferData(object target, Stream incomingData, Stream outgoingData)
    {
        var originalObject = target as SimpleExample.SomeType;

        var incomingChangedObject = Deserialize(incomingData) as SimpleExample.SomeType;

        // any changes to the object must be applied to the incoming target object instance
        originalObject.Foo = incomingChangedObject.Foo;

        //(optional) send a response message back to the Visualizer
        Serialize(outgoingData, "It worked!");            
    }

The Visualizer

For the visualizer in this simple example, I'm registering an OnChange event handler for the TextBox displaying the Foo property (the only property shown in this example) and I've removed the Readonly attribute on the textbox to allow the user to edit the value. I'm using in-line delegates as my handler, but you could just as well register explicit methods.

I'm including both approaches in this code sample, but you'll want to use one or the other, not both.

    protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
    {
        if (windowService == null)
            throw new ArgumentNullException(nameof(windowService));

        if (objectProvider == null)
            throw new ArgumentNullException(nameof(objectProvider));

        // Get the object to display a visualizer for.
        //       Cast the result of objectProvider.GetObject() 
        //       to the type of the object being visualized.
        var data = objectProvider.GetObject() as SimpleExample.SomeType;


        // Display your view of the object.        
        using (var displayForm = new SomeTypeVisualizerForm(data))
        {
            displayForm.txtFoo.Text = data.Foo;


            // Read-Write Approach 1: Using ReplaceObject
            displayForm.OnChange += (sender, newObject) => objectProvider.ReplaceObject(newObject);

            // Read-Write Approach 2: Using TransferData
            displayForm.OnChange += (sender, newObject) =>
            {
                var response = objectProvider.TransferObject(newObject) as string;
                if (!string.IsNullOrEmpty(response))
                {
                    MessageBox.Show(response, "Response", MessageBoxButtons.OK);
                }

            };

            windowService.ShowDialog(displayForm);
        }
    }

Make sure to take a look at the other posts in my Debugger Visualizer series to improve upon your visualizer.