Blazor Component Testing
Web developers are accustomed to the instant feedback the browser provides. Many frameworks even include built in mechanics for refreshing the application in real-time, or “hot reloading.” Hot reloading is often used to quickly prototype components or features for a given application. While hot reloading is certainly a tool that has its place in web development, it’s possible it is relied upon too much. Blazor has yet to provide such a tool although one is in development. An arguably better alternative is unit testing UI components. A UI component can be isolated, tested and quickly iterated upon just as application logic can be. Through unit testing we can achieve quick results without the overhead of loading the application, or even the browser. To test a Blazor component we’ll use a new framework called bUnit which is specifically designed for this purpose.
bUnit
Test Target | UI Component |
---|---|
Cost | Free |
License | Open Source |
Type | Framework |
Interface: Test Runner | Visual Studio or CLI compatible |
bUnit is a testing library for Blazor Components. Its goal is to make it easy to write comprehensive, stable unit tests. bUnit builds on top of existing unit testing frameworks such as xUnit, NUnit, and MSTest, which runs the Blazor components test as any normal unit test. bUnit runs a test in milliseconds, compared to browser-based UI tests, where a test usually takes seconds to run.
bUnit can setup and define components under tests using either C# or Razor syntax. bUnit includes methods to verify component rendering using a semantic HTML comparer. Parameters, cascading values and injecting services into components under are made easy with bUnit’s comprehensive helper methods. Even triggering event handlers and exercising a component’s interactive features is supported by the library.
Adding a bUnit test project to your application can be done several ways. One of the easiest solutions is to install the bUnit project template and create a new project using the command line.
dotnet new --install bunit.template
dotnet new bunit -o <NAME OF TEST PROJECT>
With the bUnit project created we’ll add our project or component library as a reference.
Now we can test individual components in isolation with bUnit. Since we’re already familiar with xUnit, we’ll continue with the xUnit [Fact] conventions and write our first bUnit test. A common component test is to ensure that a component initializes correctly and renders the appropriate HTML markup. This can be accomplished with bUnit’s RenderComponent<TComponent> method.
Component Unit Test Example: Initialization and Rendering
In the following example we’ll test the initial rendered Alert component. The component should render a styled div element with an internal close button. The expected rendered HTML is declared as the string expectedMarkup. Since bUnit relies on “semantic HTML comparison,” the expectedMarkup doesn’t need to be exact match, but rather an HTML equivalent match. Aspects that don’t affect component behavior or visual representation are ignored, such as: comments, insignificant white space, css class order, and implicit attributes, just to name a few.
// bUnit library
using Bunit;
// xUnit framework
using Xunit;
// Component library or Blazor application
using TestableBlazor.Client;
[Fact(DisplayName = "Initial Alert renders correct markup")]
public void AlertInit()
{
// Render an Alert component
var cut = RenderComponent<Alert>();
// Expected HTML rendered
string expectedMarkup = @"
<div class=""alert alert-danger"">
<button type=""button"" class=""close"" aria-label=""Close"">
<span aria-hidden=""true"">×</span>
</button>
</div>";
// Did it match?
cut.MarkupMatches(expectedMarkup);
}
In this test an instance of the Alert component is created, and the component lifecycle is fully completed. The final rendered component markup is then checked against the expected markup.
Component Unit Test Example: Parameter Set
bUnit can go beyond initialization rendering tests and test specific sections of markup based on component parameters. In the next example we’ll assume we have a component that has two distinct color themes represented by a CSS class. We can toggle the theme by setting the IsInfo parameter to change the CSS class. The following example renders the component with the parameter set to true and checks only the CSS class attribute on the rendered component.
[Fact(DisplayName = "Alert has info theme")]
public void AlertColor()
{
// Render the component with IsInfo = true
var cut = RenderComponent<Alert>(parameters =>
parameters.Add(p => p.IsInfo, true));
// The expected CSS value
var expectedCss = "alert alert-info";
// The actual CSS value
var actualCss = cut.Find("div").GetAttribute("class");
// Did it match?
Assert.Equal(expectedCss, actualCss);
}
In this test an instance of the Alert component is created, and the component’s IsInfo parameter set true. The rendered markup is then checked for the class attribute and the value is validated against the expected CSS class.
Simple component unit tests like these can quickly accelerate the development process. Since the application and browser are not involved the process is much faster and accurate versus manually loading the application and checking the rendered output visually.