Writing Better Assertions: Stop Using Assert.True for Expressions

The Problem: Using Assert.True For Expressions

One thing I keep running into in codebases lately (especially those where the team was new to writing automated tests), is assertions using expressions with Assert.True (or equivalent depending on your framework or language). The problem here is consider the following code:

[Fact]
public void AddShouldSumNumbers()
{
var result = CalculatorService.Add(1, 1);
Assert.True(result == 3);
}

At quick glance, you can probably tell that this assertion will fail, because 1+1 is not equal to 3. Let’s take a look at the test output:

Cool, we found out it failed… now what? How do we debug what the problem is? There’s very little useful information here. All we know is that result is not equal to 3, but we have no clue what the value of result even is. We would have to either set a break point to inspect the value or add some sort of logging.

The Solution: Use Assert.Equal

The solution to this is using Assert.Equal (or equivalent) that will automatically log out the actual value for you in the event the test fails. So the test output for this code:

[Fact]
public void AddShouldSumNumbers()
{
var result = CalculatorService.Add(1, 1);
Assert.Equal(3, result);
}

Will yield this result:

Awesome, now we know that result is 2 without having to do any extra work!

Next Blog Post: Use Something Other Than Assert.Equal

There are still improvements we can make to Assert.Equal which is to say, using something else entirely, but that’ll be for the next blog post. 😄

Using dotnet watch run with JetBrains Rider

What is dotnet watch?

dotnet watch is a way to immediately trigger a dotnet command when a file changes. The most common uses for this are using it to automatically re-run your application (using dotnet watch run) or automatically re-run your tests (using dotnet watch test) after a file change. This obviously speeds up your workflow so you don’t have to restart your server or your tests manually.

A new feature of dotnet watch run in .NET 5+ is that it will automatically launch a browser and auto-refresh the browser after it detects a change and finishes compiling (if your application has a UI).

What is JetBrains Rider?

JetBrains Rider is a cross-platform .NET IDE from the people at JetBrains (who make many developer productivity tools such as Resharper for Visual Studio, TeamCity, IntelliJ, and more). It is my go-to IDE now, due to all of its productivity enhancements over the base install of Visual Studio. I’ve been a user of Resharper for years, but Resharper and Visual Studio never seemed to play very nice together and ended up slowing down Visual Studio significantly. I put up with it due to all the extra functionality Resharper provided.

With Rider, I get all the benefits of Resharper and it’s fast. I can use Rider on Windows or macOS (which I bounce between for personal and professional work), and a lot of features are included for $150 that I would have to spend thousands to get in Visual Studio Enterprise (such as Continuous Testing).

How do I integrate dotnet watch and Rider?

Alright, now for the part you came here for. Obviously, you could run dotnet watch run directly using the terminal, but it’d be nice to have this as a launch configuration option right in Rider that is only a CTRL + F5 away. Here’s how to do that:

  1. Open your solution in Rider
  2. Select your Configuration and hit Edit Configurations
Edit Configurations image
  1. Click the Plus in the top left to Add New Configuration
  1. Choose Native Executable (Note: you CAN search)
  1. Give it a Name, I called mine dotnet watch
  2. For “Exe path” choose C:\Program Files\dotnet\dotnet.exe if you’re on Windows or /usr/local/share/dotnet/dotnet if you’re on macOS
  3. For “Program arguments” type watch run
  4. For “Working directory” choose the directory that your application’s csproj resides in.
  5. For “Environment variables” You could add ASPNETCORE_ENVIRONMENT=Development if it’s an ASP.NET Core app, but the environment variables defined in your launchSettings.json will take precedence (under the node that contains "command name": "project").
  6. The final output should look like this:
  1. Hit OK
  1. Now start the app with your new configuration selected
  1. That’s it! You’ll notice that your Run tab of Rider will now show your watch command running

That’s it!

I hope this helps someone else. An example of this in action is below:

Note: auto-attaching the debugger does not work with this option in Rider. The issue to track that is here if you want to give that a thumbs up to vote for JetBrains to work on that feature in an upcoming release.

Blazor Tuesday Tip: Test User Behavior Not Implementation Details With bUnit

As Blazor starts to gain popularity, I see some developers making a common mistake about what they should be testing with bUnit, a testing library for Blazor.

 

Tip: Test user behavior, not implementation details

To quote Kent C Dodds, author of react-testing-library (a library similar to bUnit but for React):

The more your tests resemble the way your software is used, the more confidence they can give you.

This mantra of course applies to any testing that we do, whether client-side or server-side.  The more you rely on mocks or start testing implementation details, the less confidence you will have that your software behaves how you expect it to.  Moreover, it can make refactoring hard in the future if your tests are tightly coupled with implementation details, because any change in code results in broken tests.

 

What’s a practical application of this with Blazor?

Let’s say we have a Counter component that I’m sure you’ve seen a million times.  It’s a simple component that simply increments the count by 1 every time a button is clicked:

@page "/counter"
<h1>Counter</h1>
<p id="current-count">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
public int CurrentCount { get; set; }
private void IncrementCount()
{
CurrentCount++;
}
}

view raw
Counter.razor
hosted with ❤ by GitHub

 

So we want a test for when the button is clicked, the count is incremented, right?  If you take that test criteria literally, then you might write something like this (note: we’d likely want another test to assert that the initial value is 0):

public class CounterTests : TestContext
{
[Fact]
public void ShouldIncrementCountWhenButtonIsClicked()
{
var counterComponent = RenderComponent<Counter>();
counterComponent.Find("button").Click();
// This assertion tests that the state has incremented by 1
counterComponent.Instance.CurrentCount.Should().Be(1);
}
}

view raw
CounterTests.cs
hosted with ❤ by GitHub

Run it and it works!  We’re done, right?  Wrong.  This test is testing the implementation detail that CurrentCount is stored in a property of the Countercomponent which is irrelevant to our expected user behavior.

 

What’s wrong with this assertion?

This test gives us little confidence that the component actually does what we want it to do.  We want to be sure that the CurrentCount is displayed on the screen and increments when the button is clicked.  That assertion does not guarantee CurrentCount is even displayed on the page.

Moreover, this test is brittle.  This test breaks if we change our implementation of where CurrentCount is stored (such as a local variable, injected from DI, local storage, etc.), because our test is tied to our implementation of CurrentCount being a property.

 

What’s the solution?

Focus on the user behavior.  The behavior we expect is we want to make sure the count increments on the screen, so let’s query the component itself and evaluate the value.

public class CounterTests : TestContext
{
[Fact]
public void ShouldIncrementCountWhenButtonIsClicked()
{
var counterComponent = RenderComponent<Counter>();
counterComponent.Find("button").Click();
// Asserts actual user behavior
counterComponent.Find("#current-count").TextContent.Should().Be("Current count: 1");
}
}

view raw
CounterTests.cs
hosted with ❤ by GitHub

Now our test assures us gives us more confidence, because we are asserting we are displaying the count on the screen AND it’s less brittle for future changes.   It doesn’t matter if we pivot to use some Redux-style state management library or just use a local variable, this test will pass either way.

 

Hope this helps!  For more reading on avoiding testing implementation details, you can read Kent C. Dodds’ post on Testing Implementation Details.

Globally Require Authenticated Users By Default Using Fallback Policies in ASP.NET Core

tldr;

You can use Fallback Policies in ASP.NET Core 3.0+ to require an Authenticated User by default. Conceptually, you can think of this as adding an [Authorize] attribute by default to every single Controller and Razor Page ONLY WHEN no other attribute is specified on a Controller or Razor Page (like [AllowAnonymous] or [Authorize(PolicyName="PolicyName")]).  See lines 9-11 below.

public class Startup
{
// Other Startup code omitted
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
// Register other policies here
});
// Other service registrations omitted
}
}

view raw
Startup.cs
hosted with ❤ by GitHub

https://gist.github.com/scottsauber/06950b52ca45ab0b937dc59cad19e9de#file-startup-cs-L9-L11

 

A Quick Lap Around the [Authorize] and [AllowAnonymous] Attributes

In ASP.NET Core (and even previously in ASP.NET), we’ve had the ability to add a [Authorize] attribute to a resource (such as a Controller or Razor Page) in order to tell ASP.NET Core not to let a user access that resource unless they are authenticated.

[Authorize]
public class IndexModel : PageModel
{
public void OnGet()
{
// do something
}
}

view raw
Index.cshtml.cs
hosted with ❤ by GitHub

The [Authorize] attribute can also take a PolicyName parameter that tells it what Authorization Policy to execute.  The Policy below says only Admins can access this page.

[Authorize(PolicyName="Admin")]
public class IndexModel : PageModel
{
public void OnGet()
{
// do something
}
}

view raw
Index.cshtml.cs
hosted with ❤ by GitHub

You can follow this link to learn more how to set up policies in ASP.NET Core and how to enforce your own custom rules (such as what defines an Admin).

By default, if you do not add an [Authorize] attribute, then the resource will not be secured and will be accessible to unauthenticated users.  A resource can also be accessible to unauthenticated users by explicitly adding a [AllowAnonymous] attribute.

Word of Caution: Adding the [AllowAnonymous]attribute bypasses all Authorization, and short-circuits out of the Authorization pipeline, even if Authorization is set further up the stack.

 

The Problem – Having to remember to add [Authorize] attributes everywhere

When you create a new Controller or Razor Page in ASP.NET Core, by default the resource will be accessible to anyone, because there is no [Authorize] attribute.  This is a problem if you’re creating a site where a majority of the site is protected by some sort of authentication.  It is really easy to forget to add an [Authorize] attribute which could open up your application to a security vulnerability, and leave you with a potential… let’s call it a “resume updating event.” 🙂

 

What are Fallback Policies?

ASP.NET Core 3.0 turned on Endpoint Routing by default, which was a way to get Routing information out of being tightly coupled to MVC and make Routing more global to the entire stack (such as Middleware).  The 3.0 release introduced the concept of Fallback Policies with Endpoint Routing.

A Fallback Policy means that if no other policy or attribute is specified on a Controller or Razor Page, the Authorization middleware will use the Fallback Policy.  Therefore, if you do not add any other attribute (such as [AllowAnonymous] or [Authorize(PolicyName="PolicyName")], then ASP.NET Core will use the Fallback Policy.

 

The Solution – Using a Fallback Policy to require authentication by default

So by leveraging a Fallback Policy, we can specify that a user must always be authenticated for every Controller or Razor Page in our application.  You can wire this up under ConfigureServices via the AuthorizationOptions in services.AddAuthorization.   See lines 9-11 below:

public class Startup
{
// Other Startup code omitted
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
// Register other policies here
});
// Other service registrations omitted
}
}

view raw
Startup.cs
hosted with ❤ by GitHub

Conceptually, you can think of this as adding an [Authorize] attribute by default to every single Controller and Razor Page ONLY WHEN no other attribute is specified on a Controller or Razor Page (like [AllowAnonymous] or [Authorize(PolicyName="PolicyName")]).

Of course, you could also take this one step further, by having your Fallback Policy be a policy that requires certain claims instead of just being authenticated.  The choice is up to you!

 

What do we gain by doing this?

  1. A more secure default.  A developer doesn’t have to remember to add an [Authorize] attribute to every Controller or Razor Page.
  2. Less boilerplate.  Every Controller and Razor Page requiring authentication has one less line of boilerplate code to worry about.
  3. You don’t give up any flexibility.
    1. If a Controller or Razor Page is supposed to be public to unauthenticated users (such as a Login page or Forgot Password page), then you can still add a [AllowAnonymous] attribute and the Fallback Policy is bypassed.
    2. If a Controller or Razor Page needs a specific policy, you can still add an Authorize attribute with a custom policy name.  That will take precedence over the Fallback Policy such as [Authorize(PolicyName="PolicyName")].

 

Default Policy vs. Fallback Policy

You might get confused when seeing that there’s also a “Default Policy” in addition to Fallback Policies (or at least I did).  In my head I thought “oh the Fallback Policy is kind of like the default policy that runs… but wait… what’s a Default Policy then?”

The Default Policy is the policy that gets evaluated when authorization is required, but no explicit policy is specified.  In other words, it’s the policy that evaluates when you add an [Authorize] attribute without any PolicyName.  Out of the box, the Default Policy is set to requiring Authenticated Users.

A Fallback Policy, on the other hand, is the policy that gets evaluated if no other policy is specified (such as when no [AllowAnonymous] or [Authorize] attribute exists on a Controller or Razor Page)

 

The Old Solution in ASP.NET Core 2.x

A common solution to this problem in ASP.NET Core 2.x was to add a Global Filter to MVC such as lines 10-13 below:

// OLD WAY FROM ASP.NET CORE PRIOR TO 3.0!!!!!!!!!
public class Startup
{
// Other Startup code omitted
public void ConfigureServices(IServiceCollection services)
{
// OLD WAY FROM ASP.NET CORE PRIOR TO 3.0!!!!!!!!!
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
// Other service registrations omitted
}
}

view raw
Startup.cs
hosted with ❤ by GitHub

 

Conclusion

In my opinion, the new way using a Fallback Policy makes a lot more sense. It keeps everything inside the Authorization configuration and doesn’t sprinkle Authorization logic into MVC Filters.  The only thing that’s a little goofy is the naming between a Default Policy and Fallback Policy, but once you learn that nuance, the naming makes sense.

 

In a future post, I’ll go over other tips and tricks for leveraging the ASP.NET Core authorization system.  Stay tuned.

Indented Pretty Print Formatting for JSON in ASP.NET Core

tldr;

See line 13

public class Startup
{
private readonly IWebHostEnvironment _environment;
public Startup(IWebHostEnvironment environment)
{
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = _environment.IsDevelopment());
}
// Other code ommitted for brevity
}

view raw
Startup.cs
hosted with ❤ by GitHub

 

The Problem – Readability

By default, JSON in ASP.NET Core will not be indented at all.  By that I mean, the JSON is returned without any spacing or formatting, and in the cheapest way possible (from a “bytes-sent-over-the-wire” perspective).

Example:

2019-10-29_21-32-53

 

This is usually what you want when in Production in order to decrease network bandwidth traffic.  However, as the developer, if you’re trying to troubleshoot or parse this data with your eyes, it can be a little difficult to make out what’s what.  Particularly if you have a large dataset and if you’re looking for a needle in a hay stack.

 

The Solution – Indented Formatting

ASP.NET Core ships with the ability to make the JSON format as indented (aka “pretty print”) to make it more readable.  Further, we can leverage the Environment in ASP.NET Core to only do this when in the Development environment for local troubleshooting and keep the bandwidth down in Production, as well as have the server spend less CPU cycles doing the formatting.

In ASP.NET Core 3.0 using the new System.Text.Json formatter:

public class Startup
{
private readonly IWebHostEnvironment _environment;
public Startup(IWebHostEnvironment environment)
{
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = _environment.IsDevelopment());
}
// Other code ommitted for brevity
}

view raw
Startup.cs
hosted with ❤ by GitHub

 

In ASP.NET Core 2.2 and prior:

public class Startup
{
private readonly IHostingEnvironment _hostingEnvironment;
public Startup(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options =>
{
options.SerializerSettings.Formatting = _hostingEnvironment.IsDevelopment()
? Formatting.Indented
: Formatting.None;
});
}
// Other code ommitted for brevity
}

view raw
Startup.cs
hosted with ❤ by GitHub

 

Now when I run the app, the JSON looks much cleaner and easier to read.

2019-10-29_21-56-58

Chrome Extensions

There are also Chrome Extensions that will do this for you as well when you navigate to a URL that returns an application/json Content-Type.  Some extensions add more formatting capabilities (such as colorization and collapsing).  For instance, I use JSON Formatter as my go-to extension for this.

2019-10-29_22-01-50

 

Closing – So why do I need this if I have the Chrome Extension?

While I use the JSON Formatter extension, I still like setting the indented format in ASP.NET Core for a few reasons:

  1. Not all of my team members use the same Chrome extensions I do.
  2. The formatting in ASP.NET Core will make it show up the same everywhere – the browser (visiting the API URL directly), DevTools, Fiddler, etc.  JSON Formatter only works in Chrome when hitting the API URL directly (not viewing it in DevTools).
  3. Some applications I work on use JWT’s stored in local or session storage, so JSON Formatter doesn’t help me out here.  JSON Formatter only works when I can navigate to the API URL directly, which works fine when I’m using Cookie auth or an API with no auth, but that does not work when I’m using JWT’s which don’t get auto-shipped to the server like Cookies do.

 

Hope this helps!