Blazor is a modern web framework for building single-page applications using .NET. It allows developers to build interactive client-side web UIs with C# instead of JavaScript. Unit testing is a critical part of software development, and it is particularly important in Blazor development due to the complex nature of web applications. Blazor unit testing with bUNit is something you'll want in your toolbox when you build your next app!
In this article, we'll go through a step-by-step guide on how to take on Blazor unit testing using bUnit and xUnit. I'll expand on topics such as setting up the environment, writing your first unit test, the anatomy of a unit test, and debugging and troubleshooting. By the end of this article, you'll have all the knowledge you need to tackle Blazor unit testing with bUnit and xUnit and improve the quality of your applications.
No more excuses to not have your UI tested!
Setting Up the Environment
To get started with Blazor unit testing using bUnit and xUnit, you'll need to have a few tools installed on your machine. First, you'll need a copy of Microsoft's Visual Studio integrated development environment (IDE). Visual Studio is a popular choice for C# developers, and it makes it easy to create and test Blazor applications. You should also install the .NET Core runtime, which is necessary for running and debugging .NET Core applications.
Once you have all the necessary tools installed, you can create a new Blazor project in Visual Studio. To do this, open Visual Studio and select "Create a New Project" from the start page. From there, select "Blazor App" from the list of project templates. You can choose either a client-side or server-side Blazor project, depending on your needs.
The Test Project
While there are no rules for this necessarily, I strongly encourage putting your tests into an entirely separate assembly. If it's not something you want to ship with your core code, we can pull it out into a separate project. Let's go ahead and make a second test project in our solution, a class library, and add a reference to the original project.
You'll need to add bUnit and xUnit to the test project via NuGet. NuGet is a package manager for .NET that makes it easy to share and reuse code between projects. In Visual Studio, right-click on your project in the Solution Explorer pane and select "Manage NuGet Packages". You can also do this on the command line if you're comfortable with that!
From there, you can search for both bUnit and xUnit and install them into your project. I also install "Microsoft.NET.Test.Sdk" and "xunit.runner.visualstudio" packages for a full integration right in my IDE for running tests.
Once these are installed, you're ready to start writing unit tests for your Blazor components! You can follow this article if you're having challenges discovering tests in Visual Studio.
Blazor Unit Testing with bUnit - Writing Your First xUnit Test
To get started with Blazor unit testing with bUnit and xUnit, we first need to create a basic unit test. A unit test is essentially a method that verifies that some very specific part of your code is working as intended. To create a unit test in our Blazor solution, we'll be using the xUnit testing framework alongside bUnit to unlock the access we need for Blazor components.
To write a unit test for a Blazor component, first ensure that you can access the component in your Blazor testing project. Refer to the previous section if you can't see the component as you may have forgotten to add a reference to the assembly! Then, add a new class file within this project, and name it after the component you want to test. For example, if we want to test a component called "MyComponent", we would add a new class file called "MyComponentTests.cs".
Within this class, you can create a new test method and mark it with a [Fact] attribute. A method with this attribute tells XUnit to treat it as a unit test. Within this method, we can use bUnit to create a "rendered component" instance of the component we want to test. Then use the xUnit Assert methods as well as bUnit markup checks to verify that it behaves as expected.
Example Component Testing with bUnit
For example, let's say we have a "MyComponent" that renders some HTML to the page. Let's start with what the component looks like:
<h3>My Component</h3>
<div>Hello world!</div>
@code {
}
We could write a test like this:
[Fact]
public void Header_DefaultState_ExpectedTitle()
{
var renderedComponent = RenderComponent<MyComponent>();
renderedComponent
.Find("h3")
.MarkupMatches("<h3>My Component</h3>");
renderedComponent
.Find("div")
.MarkupMatches("<div>Hello world!</div>");
}
In this test, we first create an instance of a rendered component based on the "MyComponent" class. bUnit handled the creation of the instance of the type we provide. We can then use the bUnit methods to find different markup tags to verify that the rendered HTML contains the expected <h3>
markup and <div>
markup.
By writing simple tests like this, we can check the markup for simple components very easily. It's important to note that a test like this doesn't prove that a browser renders things to look a certain way. However, consider the different responsibilities of all of the different systems that make a web app usable for an end-user. If you feel that you need to test the rendering engine in Edge or Chrome then... you'll probably want some functional tests to prove the HTML appears a certain way. Otherwise, tests like this help prove you've done your job up until that point.
Blazor Unit Testing with bUnit Video Example
I'm going to drop this video on Blazor unit testing with bUnit here because it discusses all of the same concepts that we're walking through in this article. You might find that it's a more convenient way to consume the content. Hopefully, at a minimum, it's just another way to re-inforced some of the learning that we have here:
Working With Simple Component State
It's very common for our components to have HTML on them that are based on some private state. Let's modify the previous example to pull some of the state into a variable:
<h3>My Component</h3>
<div>@message</div>
@code {
private string message = "Hello world!";
}
In the @code section, we now have a private string field called message and the div uses @message to reference that variable. While this is very simple in this example, this pattern is common where we have some private state we want to format/display.
bUnit allows us to manipulate the component state before we render it so that we can see how the markup changes. However, one of the challenges comes with the private field. bUnit can't operate on this! If we change our private field to a public property with an attribute on it, the bUnit methods can operate successfully:
@code {
[Parameter]
public string message { get; set; } = "Hello world!";
}
Now within a test method, we could write code like the following:
[Fact]
public void MessageDiv_ModifiedMessage_ExpectedResult()
{
var renderedComponent = RenderComponent<MyComponent>();
// Change state and re-render
renderedComponent.SetParametersAndRender(parameters =>
parameters.Add(p => p.message, "New message"));
// Verify updated state
renderedComponent
.Find("div")
.MarkupMatches("<div>New message</div>");
}
Without the previous modifications, SetParametersAndRender would not compile the way we have it written because it would be unable to see the message field. The attribute is also a requirement! But with these changes, we can dynamically alter the state of the component, re-render, and assert.
A Note On Access Modifier Changes
In the example that we just looked at, we changed a private field to a public property. Not only that, we slapped an attribute on it AND made it settable from outside of the component.
Did making the change from private field to mutable public property feel gross? It certainly did for me.
This isn't my preferred way to do something like this unless you have a really simple component. If you're fine creating a component where data can be mutated from other callers with a reference to your component, then it probably fits just fine. For me, it feels like if I started with a private field then it's because I likely wanted to reduce the ability to access and mutate that data.
Making a change like this strictly to be able to test is not my preferred way to address this. However, I wanted to walk through some of the capabilities of bUnit that you can take advantage of. And just because it's not my preferred way doesn't mean that it cannot or should not be used by you. If it's a good fit for you based on the trade-offs, then it's a tool that you can use.
Wrapping Up Blazor Unit Testing With bUnit & xUnit
Unit testing plays an important role in software development, allowing developers to catch bugs early on in the development process. Taking the time to properly test your code can save time and resources in the long run.
In this article, we walked through the process of setting up the necessary environment. We then checked out some code for some simple rendered component tests using bUnit where we could assert the markup. These are some of the basics that you can leverage to get started covering your components.
While this article covers the basics of Blazor unit testing with XUnit, there is always room to continue learning and improving your unit testing skills. My Dev Leader Weekly readers received exclusive access to an article discussing Blazor unit testing as well as early access to this video covering a different approach to testable Blazor components. Sign up for free today so you can get exclusive learning opportunities!
Affiliations
These are products & services that I trust, use, and love. I get a kickback if you decide to use my links. There’s no pressure, but I only promote things that I like to use!
- BrandGhost: My social media content and scheduling tool that I use for ALL of my content!
- RackNerd: Cheap VPS hosting options that I love for low-resource usage!
- Contabo: Affordable VPS hosting options!
- ConvertKit: The platform I use for my newsletter!
- SparkLoop: Helps add value to my newsletter!
- Opus Clip: Tool for creating short-form videos!
- Newegg: For all sorts of computer components!
- Bulk Supplements: Huge selection of health supplements!
- Quora: I answer questions when folks request them!