Musing, Rants & Jumbled Thoughts

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

With the popularity of Single-Page Applications (SPAs) frameworks like Vue.js, React, and Angular and static site generators like Jekyll or Hugo, website no longer need an active server like IIS or Tomcat to host the site. Instead, you just need somewhere to host the files and serve them up from a hostname.

At the end of 2018, Azure added the ability to host static websites from Azure Blob Storage. This becomes a fairly cheap way to host, since you only pay for storage (very cheap) and for network usage (also cheap). This seems like a great option for me when I recently converted a site previously running Wordpress inside an Azure Application Service. I converted the site to a SPA and wanted to host it, using a custom domain, via this new feature.

I ran some proof-of-concents and everything was working great, so I went to make the site live at the custom hostname for the existing site, including a TLS cert to enable HTTPS. This is where everything fell apart. Azure static sites support custom domains and support HTTPS, both through the use of the Azure CDN feature, and even have the ability to manage those certs automaticly via the Azure portal once you validation domain ownership.

EXCEPT for this one little note in the Prerequisites section of the Microsoft docs tuitorial for setting it up:

Important

CDN-managed certificates are not available for root or apex domains. If your Azure CDN custom domain is a root or apex domain, you must use the Bring your own certificate feature.

So if I want to have www.mydomain.com, all is good. But as soon as I want to use just mydomain.com, I can no longer use the built-in automation with Azure, and figuring out how to actually do the "Bring your own certificate" steps was a huge pain in the ass.

My goal here is to document the process I went through to enable HTTPS on a root domain for a static website in Azure storage.

I'm assuming you already have an Azure account and are somewhat familiar with the Azure portal, and that you already have a custom domain that you can manage (via Azure or a third-party provider) to add additional DNS records.

Part 1: Starting with the Static Website

Difficulty: Easy
Time required: Not much

Setting up the static website itself was pretty straightforward, using the auto-assigned domain name. The official documentation from Microsoft walks you through this, but the short version is this:

  • Create an Azure Storage Account
  • In the Azure Portal, open the Static website pane from the Settings menu for the store account
  • Click the toggle to "Enable" static websites for the storage account

This creates a $web container in the Blob storage area of the storage account. This is the folder that becomes your static site. Azure also creates two endpoints (aka hostnames) that point to this static site. Since we'll be adding a CDN for this site in a minute, we really only need to care about the "primary" endpoint.

At this point, if you created an index.html file in the $web container, you could point your browser at the primary endpoint url and it will serve up that file as if it were being hosted from a formal webserver.

In my case, I'm using VSCode to write my SPA site, and I've added the Azure Storage entension for VSCode. This allows me to easily deploy my site files to my new static website container in Azure by right-clicking on the $web container in the VSCode Azure pane and choosing "Deploy to Static Website..."

Vscode Deploy

Part 2: Adding a Content Distribution Network (CDN) Endpoint

Difficulty: Easy to Medium
Time required: Not much

Great, so we've got a static website up and running, but the website URL is https://mystorageaccountname.z14.web.core.windows.net/ and that's not what I want.

In order to use custom domains (and custom domain certs) with storage-based static sites, you must use the Azure CDN. Again, the official Microsoft documentation is well done, but the short version is this:

  • Create a CDN Profile and add a CDN endpoint that points to the "primary" endpoint of your storage account static site.

Important: You must use Azure CDN from Microsoft or Azure CDN from Verizon profiles in order to use a custom HTTPS certificate, which is required for custom root domains. I used Azure CDN from Microsoft.

Pretty simple. You'll get to provide the name you want to use, which will ultimate generate a CDN-backed URL like https:\\mycdnname.azureedge.net.

NOTE: At this point, Azure has auto-genearted HTTPS certs for each of the URLs it's generated, so HTTPS is enabled , but it's using the azureedge.net or windows.net domain names.

Part 3: Use Your Custom Domain for the CDN Endpoint

Difficulty: Easy to Medium
Time required: 5-10 minutes

Now you need to associate your custom domain with the CDN endpoint. As part of this process, Microsoft wants to make sure you actually own the domain you're attempting to use, so you have to do some verifications. There are a handful of ways to do this, including: creating DNS records with specific names under the domain, adding specifically-name html files to website on that domain, responding to an email sent to the address listed on the DNS ownership record.

For me, the easiest route was to modify the DNS records, and I'd suggest going this route for a couple of reasons:

  1. If you don't actually list your personal contact on your DNS registration (you use some privacy service, for example), then you can't use the email option
  2. With DNS, you can have the option of using verification-specific entries instead of your production entries, thus allowing you to stage your changes ahead of time without impacting production.
  3. You're going to have to change your DNS eventually in order to point your domain to the new static website, so might as well do it here.

In this case, the offical docs aren't so good, so here's a walkthrough of what I did:

Step 1: You'll need to create new CNAME or AAAA DNS records to either

  1. point cdnverify.yourdomain.com to cdnverify.yourcdnname.azureedge.net
  2. point yourdomain.com to yourcdn.azureedge.net

By using the cdnverify CNAME, you can confirm ownership without having to change the real DNS yet.

I'm using Azure as my DNS provider for this particular domain, but that's not required. You could use any DNS provider. I would suggest using a very short TTL (Time To Live -- or caching period) for the DNS record, just in case you get it wrong the first time -- otherwise you may have to wait for DNS to repropigate to fix any issue.

Cdnverify Dns

Step 2: Once you have the cdverify DNS record (or the main record) pointing to the CDN endpoint, you can move on to adding the custom domain to the CDN endpoint. In the Azure portal, pull up the CDN endpoint record and select "Custom Domains" from the navigation pane. Then click the "+ Custom domain" button.

Add Custom Domain Button

In the "Add a Custom Domain" dialog, enter the actual domain name for your CDN endpoint into the "Endpoint hostname" field (ie: youcdn.azureedge.net), and enter your custom domain name into the "Custom hostname" field. Do not use the cdnverify. hostname -- use the actual hostname.)

As you enter the "Custom hostname" value, Azure will do DNS lookups on what you enter and check that the domain is pointing at the CDN domain, or the cdnverify version of the domain is pointed at the CDN cdverify domain. If that resolution is successful, you'll get a little green checkmark at the end of the input text box. Otherwise, you'll get a red exclaimation point with a mouse-over dialog saying it couldn't do the verfication. If you get the red error mark, first double-check you didn't typo the hostname, then double-check your DNS entries.

Once you get the green checkbox, click the "Add" button.

Add Custom Domain Dialog

This will start the background process of creating the custom domain CDN associatation. This will take a minute or two to complete, after which you'll see the entry listed on the Custom domains pane, with "Disabled" in the "Custom HTTPS" column:

Custom Domain Disabled

Part 4: Enabling HTTPS for Your CDN Endpoint Custom Domain

This is where things start to get ugly.

Which Pill

You have two paths to choose here, Mr Anderson:

If you take the blue pill, you can use a non-root domain (ie: www.yourdomain.com -- note the www sub-domain), and live in a utopian land where Azure handles all the provisioning and renewals of your HTTPS certs for you for free, and you only need to set "Custom domain HTTPS" to "On" for the CDN endpoint, and select the "CDN managed" option. (Just follow the offical Microsoft walkthrough.) The story ends, you wake up in bed and believe whatever you want to believe.

If you take the red pill, you stay in Wonderland, and I show you how deep the rabbit hole goes. Remember: all I'm offering is the truth.

Ok, enough Matrix references.

At this point, you're still here because you either want to use a root domain for your static site, or maybe you have an SSL cert already that you really want to use and manage yourself. So here goes...

In order to use a root domain (like example.com), you must use the "Bring your own certificate" option. This path is not well documented, so I'm going to walk through how I achieved it.

Add your cert to the Azure Vault

Azure won't create a security cert for you for your root domain. That means you have to buy one. I'm hoping this changes, and soon, because in this era of Let's Encrypt giving everyone free certs -- and even Azure giving free certs to any non-root domain -- I don't understand the reasoning for making people pay for these root-level certs.

Do not pay hundreds of dollars for your HTTPS cert! As of Aug 2019, if you go to be "big name" providers, like Comodo (prices start at $88/year) or DigiCert (starts at $207 for 2-year cert), you'll pay too much. You do need to use an Azure-approved cert authority, but I highly suggest namecheap where you can get a single-domain HTTPS certificate for $8/year or a multi-domain cert for about $20/yr. Note: You only need a single-domain, since you only need this cert for the root domain, but in my case I went ahead and got a multi-domain and used it for both the root and www domain. Technically, I could have used a free cert from Azure for the www domain.

The process of getting a cert you can upload into Azure goes like this:

  • Create an unsigned cert in Azure Key Vault
  • Use that unsigned cert to generate a "Certificate Signing Request" (CSR) from Azure
  • Provide that CSR to your certificate authority. Go through whatever process they require to validate your request.
  • Take the signed certificate generated by the cert authority and upload it into Azure Key Vault

That's the overview. Here's the details:

Step 1: Create an unsigned cert in Azure Key Vault

Difficulty: Easy
Time required: Very little

Create an Azure Key Vault in the Resource Group with your static website and CDN instances. Within the Vault, go to the "Certificates" pane and click "Generate/Import" button.

Self Signed Cert

In the "Create a Certificate" dialog, enter the following:

  • For "Method of Certificate Creation", choose "Generate"
  • For "Certificate Name", pick any name you want to identify the record -- but remove any symbols and whitespace
  • For "Type of Certificate Authority (CA)", pick "Certificate issued by a non-integrated CA"
  • For "Subject", use CN=yourdomain.com (ie: CN= followed by your domain name)
  • Click on the "DNS Names" section and enter your domain. If you want to have multiple domains on the cert (such as the root domain and the www domain: type one, press [tab] to enter that one and get a new text box to enter the next domain. Once you have all the domains entered, click "OK"
  • For "Validity Period", pick how long you want the cert to be valid. This doesn't have to match the length you purchaged from your certificate authority -- it can't be longer, but it can be shorter. If it's shorter, you should be able to request an updated cert without charge around the time the current cert expires.
  • For "Content Type", check with your cert auth to see what they support, but I would default to PEM.
  • For the "Lifetime Action Type" and the reamining inputs, decide how you want the system to notify you when the cert is about to expire. I would suggest having it email you within about a month of expiration.
  • Click "Create"

Your cert should be created immediately, but marked as "Disabled".

Disabled Cert

Step 2: Get the cert signed by your Certificate Authority and upload it back to Azure

Difficulty: Easy
Time required: 15min or more, depending on your CA's turnaround time

Click on the cert entry to open its details, then click the "Certificate Operations" button. On the resulting dialog, click the "Download CSR" button.

Cert Download Csr

This will download a Certificate Signing Request file, which is basically all of the details that would go into a signed cert, plus some server information. If you want to see the actual data in the file, you can use this SSL & CSR decoder tool.

Now you'll need to hand this CSR file off to your certificate authority. For me, using namecheap as my certificate authority, I had to upload the CSR and then had to add more CNAME DNS entries to prove I owned the domains listed in the cert request. (They provided the instructions for what domains to create and where they should point). Once they are able to verify ownership via DNS entries, it took about 15 mins for them to inform me (via email) that my cert was ready and I was able to download a PEM file that included three entries:

  • the CSR I submitted
  • the signed cert
  • the RSA private key

BE CAREFULL WITH THIS FILE. It includes the private key, so anyone with this file could pretend to be your server. Treat it like you would a password.

You should get that same file from your cert authority. Once you have it, go back to the same screen in Azure where you created the CSR and this time choose "Merge Signed Request" and upload the file with the signed cert and private key. Merge Cert

If all goes well, you'll see the cert now listed as "Enabled" Enabled Cert

Give Azure CDN access to your Key Value

Difficulty: Medium
Time required: under 15 mins

The CDN service needs permissions to read from the Azure Key Vault in order to fetch your HTTPS cert. Unfortunately, you can't do this next step in the web portal, so you have to use some PowerShell commands.

Open a PowerShell prompt and run the following:

This will install the Azure AD CLI (Command Line Interface) for PowerShell, which is needed for the next two commands:

Install-Module AzureAD

Authenticate with Azure:

Connect-AzAccount

Give the Azure CDN service account an AD Principal in your default Azure AD instance: Note: The GUID in this command is the same for everyone -- no need to change it.

New-AzADServicePrincipal -ApplicationId "205478c0-bd83-4e1b-a9d6-db63a3e1e1c8" -Verbose

Now, back in the Azure portal, in the Key Vault, select the "Access Policy" pane and click "+ Add Access Policy"

Key Vault Add Policy

In the "Add access policy" dialog, start by clicking the "Select principal" option. Enter "Microsoft.Azure.Cdn" in the "Select" text input. Click on the "Microsoft.Azure.Cdn" entry so that it shows in the "Selected Members" list half-way down the dialog. Then click the "Select" button.

Select Principal

Back in the "Add access policy" dialog:

  • under "Secret Permissions", choose "Get" and "List"
  • under "Certificate Permissions", choose "Get" and "List"

Note: The only documentation I could find on this step only mentioned the Secret Permissions -- but it appears the Certificate Permissions were added after that doc was written, and without the cert permissions, the next steps fail. So I'm not 100% certain that the Secret permissions are required, but I do know the certs permissions are.

Now click the "Add" button to save the permissions policy.

Part 5: Associate the Signed Cert with your CDN Endpoint

Difficulty: Easy, if it "Just Works ™"
Time required: Active time: 20 mins. Waiting around time: 12-24 hours, maybe more!

Back in the Azure CDN section of the portal, select the Custom Domains panel and click on the custom domain record that currently says "Disabled" in the Custom HTTPS column.

In the Custom Domain screen, turn on the "Custom domain HTTPS" toggle and pick "Use my own certificate".

In the "Key Vault" dropdown, select your key vault. If you don't see it, then you need to double-check the permissions in the Key Vault Access Policy set in the previous section.

In the "Certificate/Secret" dropdown, pick your cert. The "Certificate/Version" input will auto-populate with the most recent entry for the cert.

Click "Save".

Apply Cert

This will kick off the process of enabling the cert for your custom domain. This has two phases:

"Importing Certficate" should take just a few minutes.

"Certificate Provisioning" will take up to 24 hours!!!!!

Each phase will show you the current status and turn to a green checkmark when completed.

Cert Progress

Part 7: Point your DNS records to the CDN

Once the Certificate Provisioning is completed, your static website is all set. Now you just need to go into your DNS provider and change the main DNS record for your custom domain to point to your CDN endpoint's address. If your provider allows you to use CNAMEs, use that, otherwise use the actual IP Address of the endpoint and an A record.

If you use Azure for your DNS provider, they provide a shortcut for A and CNAME records where you can select "Alias record set" as "Yes" and then choose "Alias Type" of "Azure Resource". This will then allow you to select the CDN endpoint you created as the source for your DNS entry. This ensures that if the CDN address ever changes, your DNS record changes with it.

Final D N S

ALL DONE!

That's it. You're all done.

When you push changes to your static website, the CDN will continue to serve up the old copy until the cache expires. You can force it to purge the cache in the CDN section of the portal by clicking the "Purge" button for the endpoint or the whole CDN instance:

Purge Cdn



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 frameworks I'm going to focus on in this post:

Initial Thoughts

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 easy 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 built-in framework 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 (though it's improving), 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.

Shouldly's main difference is that it adds extension methods off of your normal objects. So instead of calling Assert..., you would write myVar.Should.... Additionally, Shouldly uses reflection to inspect the code around your call to provide more useful information, like the variable name and LINQ statements called. However, it seems quite a bit behind where Fluent Assertions is from a maturity standpoint, and I would suggest going with Fluent Assertions over Shouldly.

Fluent Assertions comes in second place. Like Shouldly, it uses fluent-style extension methods instead of Assert.That... calls. While I haven't used it (yet) on a production codebase, just "playing" with it for this blogpost and related GitHub repo has shown me that it's an easy to use and full-featured framework. And it's documentation is very well done. I highly suggest you give it a review if you're getting started with a project.

All of these 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 other test framework built-in options -- 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)).

Both Shouldly and Fluent Assertions take a different approach and use a fluent-style syntax that starts with the actual value from your test execution and compares it to the expected values in an English-like way. (ex "myValue should be greater than 0"). They also use Reflection to try and provide additional context in the error messages upon failure.

A word about custom messages

All of the assertion options except xUnit.net 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>.
/// <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 }; ;


    valueToTest_bool.Should().Be(true);
    valueToTest_string.Should().Be("some result");
    valueToTest_datetime.Should().Be(new DateTime(2019, 01, 01));
    valueToTest_obj.Should().Be(expectedValue_obj_equal);

    valueToTest_string.Should().NotBe("some other result");
    valueToTest_datetime.Should().NotBe(new DateTime(2019, 12, 01));
    valueToTest_obj.Should().NotBe(expectedValue_obj_notequal);

}
Sample Output:
Expected valueToTest_obj to be { Foo = zoom, Baz = False }, but found { Foo = bar, Baz = True }.


Expected valueToTest_datetime not to be <2019-01-01>, but it is.   


Expected valueToTest_string to be 
"some other result" with a length of 17, but 
"some result" has a length of 11, differs near "res" (index 5).
/// <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 }; ;


    valueToTest_bool.ShouldBe(true);
    valueToTest_string.ShouldBe("some result");
    valueToTest_datetime.ShouldBe(new DateTime(2019, 01, 01));
    valueToTest_obj.ShouldBe(expectedValue_obj_equal);

    valueToTest_bool.ShouldNotBe(false);
    valueToTest_string.ShouldNotBe("some other result");
    valueToTest_datetime.ShouldNotBe(new DateTime(2019, 12, 01));
    valueToTest_obj.ShouldNotBe(expectedValue_obj_notequal);
}
Sample Output:
Shouldly.ShouldAssertException : valueToTest_obj
    should be
{ Foo = zoom, Baz = False }
    but was
{ Foo = bar, Baz = True }


Shouldly.ShouldAssertException : valueToTest_datetime
    should be
2019-02-01T00:00:00.0000000
    but was
2019-01-01T00:00:00.0000000


Shouldly.ShouldAssertException : valueToTest_string
    should be
"some other result"
    but was
"some result"
    difference
Difference     |                           |    |    |    |    |    |    |    |    |    |    |    |
               |                          \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/
Index          | 0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15   16
Expected Value | s    o    m    e    \s   o    t    h    e    r    \s   r    e    s    u    l    t
Actual Value   | s    o    m    e    \s   r    e    s    u    l    t
Expected Code  | 115  111  109  101  32   111  116  104  101  114  32   114  101  115  117  108  116
Actual Code    | 115  111  109  101  32   114  101  115  117  108  116
Note that the string comparison has an "arrow" showing where each difference occurred.

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.
/// <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 }; ;

    valueToTest.Should().BeSameAs(expectedValue_same);
    valueToTest.Should().NotBeSameAs(expectedValue_notsame);

}
Sample Output:
Expected valueToTest to refer to 
{ Foo = bar, Baz = True }, but found 
{ Foo = bar, Baz = True }.


Did not expect valueToTest to refer to 
{ 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 }; ;

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

}
Sample Output:
Shouldly.ShouldAssertException : valueToTest
    should be same as
{ Foo = bar, Baz = True }
    but was
{ Foo = bar, Baz = True }


Shouldly.ShouldAssertException : valueToTest
    should not be same as
{ Foo = bar, Baz = True }
    but was
{ Foo = bar, Baz = True }

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

    valueToTest.Foo.Should().BeNull();
    valueToTest.Baz.Should().NotBeNull();
}
Sample Output:
Expected valueToTest.Foo not to be <null>.

Expected valueToTest.Baz to be <null>, but found System.Object (HashCode=35410979).
/// <summary>
/// Assertions that test for null values
/// </summary>
[Test]
public void NullChecks()
{
    var valueToTest = new { Foo = (object) null, Baz = new object() };


    // Constraint-style asserts:
    valueToTest.Foo.ShouldBeNull();
    valueToTest.Baz.ShouldNotBeNull();

}
Sample Output:
Shouldly.ShouldAssertException : valueToTest.Baz
    should be null but was
System.Object (35567111)

Shouldly.ShouldAssertException : valueToTest.Foo
    should not be null but was

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 and Fluent Assertions 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 and Fluent Assertions 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.
/// <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;

    bool trueValue = true;
    bool falseValue = false;

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

   
    bigNumber.Should().BeGreaterThan(smallNumber);
    bigNumber.Should().BeGreaterOrEqualTo(smallNumber);

    smallNumber.Should().BeLessThan(bigNumber);
    smallNumber.Should().BeLessOrEqualTo(bigNumber);

    trueValue.Should().BeTrue();
    falseValue.Should().BeFalse();

    bigNumber.Should().BePositive();
    smallNumber.Should().BeNegative();

    zero.Should().BeInRange(-100, 5);
    zero.Should().NotBeInRange(1, 10);
    jan1.Should().BeAfter(new DateTime(2018, 01, 01)).And.BeBefore(new DateTime(2019, 12, 31));

    zero.Should().BeOneOf(42, 0, 100);

    2.333333d.Should().BeApproximately(2.3, 0.5);
    jan1.Should().BeCloseTo(new DateTime(2019, 01, 10), 10.Days());
   
}
Sample Output:
Expected bigNumber to be less than -2147483648, but found 2147483647.

Expected smallNumber to be greater or equal to 2147483647, but found -2147483648.

Expected trueValue to be false, but found True.

Expected smallNumber to be positive, but found -2147483648.

Expected zero to be between 1 and 10, but found 0.

Expected jan1 to be after <2018-01-01>, but found <2017-01-01>.

Expected zero to be one of {42, 0, 100}, but found 33.

Expected floatNum to approximate 5.3 +/- 0.5, but 2.333333 differed by 2.9666669999999997.

Expected jan1 to be within 10d from <2019-01-10>, but found <2019-11-01>.
/// <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;

    bool trueValue = true;
    bool falseValue = false;

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

    bigNumber.ShouldBeGreaterThan(smallNumber);
    bigNumber.ShouldBeGreaterThanOrEqualTo(smallNumber);

    smallNumber.ShouldBeLessThan(bigNumber);
    smallNumber.ShouldBeLessThanOrEqualTo(bigNumber);

    trueValue.ShouldBeTrue();
    falseValue.ShouldBeFalse();

    bigNumber.ShouldBePositive();
    smallNumber.ShouldBeNegative();

    zero.ShouldBeInRange(-100, 5);
    zero.ShouldNotBeInRange(1, 10);
    jan1.ShouldBeInRange(new DateTime(2018, 01, 01), new DateTime(2019, 12, 31));

    zero.ShouldBeOneOf(42, 0, 100);

    2.333333d.ShouldBe(2.3, 0.5);
    jan1.ShouldBe(new DateTime(2019, 01, 10), TimeSpan.FromDays(10));

}
Sample Output:
Shouldly.ShouldAssertException : bigNumber
    should be less than
-2147483648
    but was
2147483647

Shouldly.ShouldAssertException : trueValue
    should be
False
    but was
True

Shouldly.ShouldAssertException : zero
    should be in range
{ from = 1, to = 10 }
    but was
0

Shouldly.ShouldAssertException : jan1
    should not be in range
{ from = 1/1/2018 12:00:00 AM, to = 12/31/2019 12:00:00 AM }
    but was
2019-01-01T00:00:00.0000000

Shouldly.ShouldAssertException : zero
    should be one of
[42, 0, 100]
    but was
33

Shouldly.ShouldAssertException : myNum
    should be within
0.5d
    of
2.3d
    but was
1.2222222d

Shouldly.ShouldAssertException : jan1
    should not be within
10.00:00:00
    of
2019-01-10T00:00:00.0000000
    but was
2019-01-01T00:00:00.0000000

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$'. .
/// <summary>
/// String-specific checks
/// </summary>
[Test]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";
  
    
    "".Should().BeEmpty();
    valueToTest.Should().NotBeEmpty();
    valueToTest.Should().Contain("Bar");
    valueToTest.Should().NotContain("Bang");
    valueToTest.Should().StartWith("Foo");
    valueToTest.Should().NotStartWith("Bar");
    valueToTest.Should().EndWith("Bin");
    valueToTest.Should().NotEndWith("Baz");
    valueToTest.Should().BeEquivalentTo("foo bar baz bin");
    valueToTest.Should().NotBeEquivalentTo("something else");
    valueToTest.Should().MatchRegex("^Foo.*Bin$"); // param is a regex pattern
    valueToTest.Should().NotMatchRegex("^Foo.*Bar$"); // param is a regex pattern
    valueToTest.Should().Match("Foo*Bin"); // param is a wildcard pattern
    valueToTest.Should().NotMatch("Foo*Bar"); // param is a wildcard pattern


}
Sample Output:
Expected string to be empty, but found "asdf".

Did not expect valueToTest to be empty.

Did not expect valueToTest "Foo Bar Baz Bin" to contain "Bar".

Expected valueToTest "Foo Bar Baz Bin" to contain "Bang".

Expected valueToTest that does not start with 
"Foo", but found 
"Foo Bar Baz Bin".

Expected valueToTest to start with 
"Bar", but 
"Foo Bar Baz Bin" differs near "Foo" (index 0).

Expected valueToTest "Foo Bar Baz Bin" not to end with "Bin".

Expected valueToTest "Foo Bar Baz Bin" to end with "Baz".

Expected valueToTest not to be equivalent to "foo bar baz bin", 
but they are.

Expected valueToTest to be equivalent to 
"something else" with a length of 14, but 
"Foo Bar Baz Bin" has a length of 15, differs near "Foo" (index 0).

Did not expect valueToTest to match regex 
"^Foo.*Bin$", but 
"Foo Bar Baz Bin" matches.

Expected valueToTest to match 
"Foo*Bar", but 
"Foo Bar Baz Bin" does not.
/// <summary>
/// String-specific checks
/// </summary>
[Test]
public void StringChecks()
{
    var valueToTest = "Foo Bar Baz Bin";

    string.Empty.ShouldBeEmpty();
    valueToTest.ShouldNotBeEmpty();
    valueToTest.ShouldContain("Bar");
    valueToTest.ShouldNotContain("Bang");
    valueToTest.ShouldStartWith("Foo");
    valueToTest.ShouldNotStartWith("Bar");
    valueToTest.ShouldEndWith("Bin");
    valueToTest.ShouldNotEndWith("Baz");
    valueToTest.ShouldMatch("^Foo.*Bin$"); // param is a regex pattern
    valueToTest.ShouldNotMatch("^Foo.*Bar$"); // param is a regex pattern

}
Sample Output:
Shouldly.ShouldAssertException : valueToTest
    should be empty but was
"Foo Bar Baz Bin"

Shouldly.ShouldAssertException : valueToTest
    should not be empty but was

Shouldly.ShouldAssertException : valueToTest
    should not contain (case insensitive comparison)
"Bar"
    but was actually
"Foo Bar Baz Bin"

Shouldly.ShouldAssertException : valueToTest
    should contain (case insensitive comparison)
"Bang"
    but was actually
"Foo Bar Baz Bin"


Shouldly.ShouldAssertException : valueToTest
    should not start with
"Foo"
    but was
"Foo Bar Baz Bin"


Shouldly.ShouldAssertException : valueToTest
    should start with
"Bar"
    but was
"Foo Bar Baz Bin"


Shouldly.ShouldAssertException : valueToTest
    should not end with
"Bin"
    but was
"Foo Bar Baz Bin"


Shouldly.ShouldAssertException : valueToTest
    should end with
"Baz"
    but was
"Foo Bar Baz Bin"



Shouldly.ShouldAssertException : valueToTest should not match "^Foo.*Bin$" but did

Shouldly.ShouldAssertException : valueToTest
    should match
"^Foo.*Bar$"
    but was
"Foo Bar Baz 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[]>.
/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[Test]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] { };


    stringList.Should().BeOfType(typeof(List<string>));
    stringList.Should().BeOfType<List<string>>();

    intEnumerable.Should().NotBeOfType(typeof(List<int>));
    intEnumerable.Should().NotBeOfType< List<int>>();

    stringList.Should().BeAssignableTo(typeof(IEnumerable<string>));
    stringList.Should().BeAssignableTo<IEnumerable<string>>();

    stringList.Should().NotBeAssignableTo(typeof(string[]));
    stringList.Should().NotBeAssignableTo<string[]>();

}
Sample Output:
Expected type not to be [System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089], but it is.
  
Expected type to be System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], but found System.Int32[].


Expected stringList to not be assignable to System.Collections.Generic.IEnumerable`1[System.String], but System.Collections.Generic.List`1[System.String] is.

Expected stringList to be assignable to System.String[], but System.Collections.Generic.List`1[System.String] is not.
/// <summary>
/// Tests related to object types and inheritance
/// </summary>
[Test]
public void TypeChecks()
{
    IList<string> stringList = new List<string>();
    IEnumerable<int> intEnumerable = new int[] {1, 2, 3};

    stringList.ShouldBeAssignableTo(typeof(IEnumerable<string>));
    stringList.ShouldBeAssignableTo<IEnumerable<string>>();

    stringList.ShouldNotBeAssignableTo(typeof(string[]));
    stringList.ShouldNotBeAssignableTo<string[]>();


    intEnumerable.ShouldBeOfType(typeof(int[])); //must be exact type
    intEnumerable.ShouldBeOfType<int[]>(); //must be exact type


    stringList.ShouldNotBeOfType(typeof(IEnumerable<string>)); //must be exact type
    stringList.ShouldNotBeOfType<IEnumerable<string>>(); //must be exact type

}
Sample Output:
Shouldly.ShouldAssertException : stringList
    should not be assignable to
System.Collections.Generic.IEnumerable`1[System.String]
    but was
[] (System.Collections.Generic.List`1[System.String])


Shouldly.ShouldAssertException : stringList
    should be assignable to
System.String[]
    but was
System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]



Shouldly.ShouldAssertException : intEnumerable
    should not be of type
System.Int32[]
    but was
[1, 2, 3]

Shouldly.ShouldAssertException : stringList
    should be of type
System.Collections.Generic.IEnumerable`1[System.String]
    but was
System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

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.
/// <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);


    stringArr.Should().ContainItemsAssignableTo<string>();
    intList.Should().Contain(x => x >= 0);
    objArr.Should().NotContainNulls();

    intList.Should().OnlyHaveUniqueItems();


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

    stringArr.Should().BeEquivalentTo(new string[] { "bar", "baz", "", "bin", "foo" });
    stringArr.Should().NotBeEquivalentTo(new string[] { "bar", "baz" });

    stringArr.Should().Contain("foo");
    stringArr.Should().NotContain("zoom");

    Enumerable.Range(5, 20).Should().BeSubsetOf(intList);
    Enumerable.Range(-1, 1).Should().NotBeSubsetOf(intList);

    new int[] { }.Should().BeEmpty();
    intList.Should().NotBeEmpty();

    new int[] { 1, 2, 3 }.Should().BeInAscendingOrder();
    new int[] { 2, 1, 3 }.Should().NotBeInDescendingOrder();

    string[] sarray = new string[] { "a", "aa", "aaa" };
    sarray.Should().BeInAscendingOrder(new StringLengthComparer());

    intList.Should().HaveCount(100);

    intList.Should().OnlyContain(x => x >= 0);
   
}

private class StringLengthComparer : IComparer<object>
{
    public int Compare(object x, object y)
    {
        if (x == null || y == null)
        {
            if (x == y) return 0;

            if (x == null)
                return -1;
            else return 1;
        }

        if (x is string xs && y is string ys)
        {
            return xs.Length.CompareTo(ys.Length);
        }
        else
        {
            return -1;
        }
    }
}
Sample Output:
Expected stringArr to contain only items of type System.Int32, but item "foo" at index 0 is of type System.String.

Expected intList to contain only items matching (x >= 3), but {0, 1, 2} do(es) not match.

Expected objArr not to contain <null>s, but found one at index 3.

Expected intList to only have unique items, but items {0, 1, 3} are not unique.

Expected stringArr to be a collection with 5 item(s), but {"foo", "bar", "baz", "bin", "", "extra"}
contains 1 item(s) more than
{"bar", "baz", "", "bin", "foo"}.

Expected stringArr {"foo", "bar", "baz", "bin", ""} not be equivalent with collection {"bar", "baz", "", "bin", "foo"}.

Expected stringArr {"foo", "bar", "baz", "bin", ""} to contain "extra".

Expected Enumerable.Range(-5, 20) to be a subset of {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, …68 more…}, but items {-5, -4, -3, -2, -1} are not part of the superset.

Expected collection to be empty, but found {1, 2, 3}.

Expected collection to contain items in descending order, but found {2, 1, 3} where item at index 0 is in wrong order.

Expected sarray to contain items in ascending order, but found {"a", "aaa", "aa"} where item at index 1 is in wrong order.

Expected intList to contain 50 item(s), but found 100.
Note that you have to create your own IComparer for BeIn*Order if you sort by anything other than the natural instance sorting
/// <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);


    stringArr.ShouldAllBe(x => x is string);
    intList.ShouldAllBe(x => x >= 0);
    objArr.ShouldAllBe(x => x != null);

    intList.ShouldBeUnique();


    intList.ShouldBe(Enumerable.Range(0, 100));
    intList.ShouldNotBe(Enumerable.Range(1, 5));

    stringArr.ShouldBe(new string[] { "bar", "baz", "", "bin", "foo" }, true);

    stringArr.ShouldContain("foo");
    stringArr.ShouldNotContain("zoom");

    Enumerable.Range(5, 20).ShouldBeSubsetOf(intList);

    new int[] { }.ShouldBeEmpty();
    intList.ShouldNotBeEmpty();

    var intArr = new int[] { 1, 2, 3 };
    intArr.ShouldBeInOrder();


    string[] sarray = new string[] { "a", "aa", "aaa" };
    sarray.ShouldBeInOrder(SortDirection.Ascending, new StringLengthComparer());

    intList.Count().ShouldBe(100);

    intList.ShouldAllBe(x => x >= 0);


}


private class StringLengthComparer: IComparer<string> {
    public int Compare(string x, string y)
    {

        if (x == null || y == null)
        {
            if (x == y) return 0;

            if (x == null)
                return -1;
            else return 1;
        }

        return x.Length.CompareTo(y.Length);
    }
}
Sample Output:
Shouldly.ShouldAssertException : stringArr
    should satisfy the condition
(x Is Int32)
    but
["foo", "bar", "baz", "bin", ""]
    do not

Shouldly.ShouldAssertException : intList
    should satisfy the condition
(x >= 3)
    but
[0, 1, 2]
    do not


Shouldly.ShouldAssertException : intList
    should be unique but
[1]
    was duplicated


Shouldly.ShouldAssertException : stringArr
    should be (ignoring order)
["bar", "baz", "", "bin", "foo"]
    but
["bar", "baz", "", "bin", "foo"]
    is missing
["extra"]



Shouldly.ShouldAssertException : stringArr
    should contain
"extra"
    but was actually
["foo", "bar", "baz", "bin", ""]


Shouldly.ShouldAssertException : myList
    should be subset of
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    but
[-5, -4, -3, -2, -1]
    are outside subset

Shouldly.ShouldAssertException : intList
    should be empty but had
100
    items and was
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]


Shouldly.ShouldAssertException : intArr
    should be in ascending order but was not.
    The first out-of-order item was found at index 1:
1

Shouldly.ShouldAssertException : sarray
    should be in ascending order but was not.
    The first out-of-order item was found at index 2:
aa

Shouldly.ShouldAssertException : intList.Count()
    should be
50
    but was
100
Note that you have to create your own IComparer for ShouldBeInOrder if you sort by anything other than the natural instance sorting

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!
/// <summary>
/// Exception-specific checks
/// </summary>
[Test]
public void ExceptionChecks()
{

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


    Action act = () => { return; };
    act.Should().NotThrow();

    act = () => MethodThatThrows();

    act.Should().Throw<ArgumentException>();

    act = () => throw new Exception("message");

    act.Should().Throw<Exception>().And.Message.Should().Be("message");

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

}
Sample Output:
Did not expect any exception, but found System.ArgumentException with message "Value does not fall within the expected range."

Expected a <System.ArgumentException> to be thrown, but found <System.NotImplementedException>: "
"System.NotImplementedException with message "The method or operation is not implemented."

Expected act to be "message" with a length of 7, but "opps!" has a length of 5, differs near "opp" (index 0).

Expected type to be System.NotImplementedException, but found System.ApplicationException.
/// <summary>
/// Exception-specific checks
/// </summary>
[Test]
public void ExceptionChecks()
{

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

    void MethodThatDoesNotThrow() { return;}


    Action actionThatThrows = MethodThatThrows;
    Action actionThatDoesNotThrow = MethodThatDoesNotThrow;

    actionThatThrows.ShouldThrow<ArgumentException>();
    actionThatThrows.ShouldThrow(typeof(ArgumentException));
    actionThatDoesNotThrow.ShouldNotThrow();

    var ex = ((Action)(() => throw new Exception("message")))
        .ShouldThrow<Exception>();
    ex.Message.ShouldBe("message");

}
Sample Output:
Shouldly.ShouldAssertException : `actionThatThrows()`
    should not throw but threw
System.ArgumentException
    with message
"Value does not fall within the expected range."

Shouldly.ShouldAssertException : `actionThatDoesNotThrow()`
    should throw
System.ArgumentException
    but did not

Shouldly.ShouldAssertException : `actionThatThrows()`
    should throw
System.NotImplementedException
    but threw
System.ArgumentException



Shouldly.ShouldAssertException : ex.Message
    should be
"message"
    but was
"different message"
    difference
Difference     |  |    |    |    |    |    |         |    |    |    |    |    |    |    |    |    |
               | \|/  \|/  \|/  \|/  \|/  \|/       \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/  \|/
Index          | 0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15   16
Expected Value | m    e    s    s    a    g    e
Actual Value   | d    i    f    f    e    r    e    n    t    \s   m    e    s    s    a    g    e
Expected Code  | 109  101  115  115  97   103  101
Actual Code    | 100  105  102  102  101  114  101  110  116  32   109  101  115  115  97   103  101

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

}

Multiple Criteria Assert

The NUnit Constraint-style syntax, Shouldly, and Fluent Assertions all allow you to chain assert conditions together, 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
/// <summary>
/// Syntax for executing multiple assertions in the same test (ie: all asserts are run)
/// </summary>
[Test]
public void MultipleCriteriaChecks()
{
    var aNumber = 5.0;

    aNumber.Should().BeGreaterOrEqualTo(0).And.BeLessOrEqualTo(10);
    aNumber.Should().BeOfType(typeof(double)).And.BeInRange(0.0, 10.0);

}
Sample Output:
Expected aNumber to be less or equal to 10.0, but found 15.0.
/// <summary>
/// Syntax for executing multiple assertions in the same test (ie: all asserts are run)
/// </summary>
[Test]
public void MultipleCriteriaChecks()
{
    double aNumber = 5.0;

    aNumber.ShouldSatisfyAllConditions(
        () => aNumber.ShouldBeAssignableTo<double>(),
        () => aNumber.ShouldBeGreaterThanOrEqualTo(0.0),
        () => aNumber.ShouldBeLessThanOrEqualTo(10.0)
    );

}
Sample Output:
Shouldly.ShouldAssertException : aNumber
    should satisfy all the conditions specified, but does not.
The following errors were found ...
--------------- Error 1 ---------------
    aNumber
        should be assignable to
    System.Int32
        but was
    System.Double

--------------- Error 2 ---------------
    aNumber
        should be greater than or equal to
    0d
        but was
    -5.1d

-----------------------------------------

Multiple Asserts

Normally, once an Assert fails, execution of the test halts and no additional Asserts are attempted. NUnit provides Assert.Multiple, and Fluent Assertions provides AssertionScope. You can use each to wraps your Assert calls and it 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
/// <summary>
/// Syntax for executing multiple assertions in the same test (ie: all asserts are run)
/// </summary>
[Test]
public void MultipleCriteriaChecks()
{
    var aNumber = 5.0;

    using (new AssertionScope()) 
    {
        aNumber.Should().BeOfType(typeof(double));
        aNumber.Should().BeInRange(0.0, 10.0);
    }
    
}
Sample Output:
Expected type to be System.String, but found System.Double.
Expected aNumber to be between 0.0 and 10.0, but found 15.0.

NUnit Specialized Asserts

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

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"

Fluent Assertions Extras

Fluent Assertions provides a whole lot more than what I've covered here, including assertions for enums, nullable types, dictionaries, guids, plus an extensibility API that allows you to easily introduction your own custom assertions.

All of these are documented well at https://fluentassertions.com/introduction



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.