How-To: Write bUnit Test for Component

bUnit is a testing library for Blazor components. Its goal is to make it easy to write comprehensive, stable unit tests for components. We will learn the steps to create a bUnit test class from setup to rendering to validation. This test will focus on testing a static component with a simple rendering operation.

First, we must have a Blazor project and corresponding test project. Either open an existing solution or please follow the steps in the How-To: Add bUnit Test Project to Blazor Solution article to get both projects and all of the required packages. For this article, we called the projects: Blazor.HowTo and Blazor.HowTo.Tests.

Then, we need a Blazor component in our project. We can create one using the steps in the How-To: Create New Blazor Component article.

Create New Test Class

To verify the behavior of our static BannerAd component, we are going create a corresponding test class in the Blazor.HowTo.Tests project by following these steps:

  1. Create a Components folder in the test project. We want our test project structure to follow our source project to make finding corresponding classes and tests easier.
  2. Right-click on the Components folder, and select the ‘Add > Class’ menu item.
Fig 1 – Add New Item Dialog
  1. Name the class file: BannerAdTests.cs. We follow the pattern of appending ‘Tests’ to the class name that is being tested. Again to make mapping test classes and implementation classes straightforward.
  2. Finally, click the ‘Add’ button.

This will result in the creation of a normal C# class with the specified name. The xUnit test framework just uses plain old C# objects to implement its tests.

Write First Component Test Method

Let’s implement the code for our first test to verify that the BannerAd component renders the HTML we expect. As a reminder, here is the code for the BannerAd component:

<div class="col-12 pt-1 rounded border" style="background-color: lightgray">
    <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-5.0">
        <img class="float-left mr-2 mt-2 align-items-center" src="/images/blazor-ad-image.png" />
    </a>
    <h5><strong>Learn About Blazor!</strong></h5>
    <p style="font-size: small">
        Learn about the .NET 5 technology for building single page applications (SPAs).
        Transfer your knowledge of C# and .NET into the exciting realm of frontend development.
    </p>
</div>

Since it currently only has static HTML and no logic, databinding, or interactivity, we only need one test to ensure it renders as expected. So, here is the code for our test:

using Blazor.HowTo.Components;
using Bunit;
using Xunit;

namespace Blazor.HowTo.Tests.Components
{
    public class SimpleAdTests
    {
        [Fact]
        public void InitialRender()
        {
            // arrange
            var ctx = new TestContext();

            // act
            var comp = ctx.RenderComponent<BannerAd>();

            // assert
            var expectedHtml =
@"  <div class=""col-12 pt-1 rounded border"" style=""background-color: lightgray"">
      <a href=""https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-5.0"">
        <img class=""float-left mr-2 mt-2 align-items-center"" src=""/images/blazor-ad-image.png"">
      </a>
      <h5>
        <strong>Learn About Blazor!</strong>
      </h5>
      <p style=""font-size: small"">
        Learn about the .NET 5 technology for building single page applications (SPAs).
        Transfer your knowledge of C# and .NET into the exciting realm of frontend development.
      </p>
    </div>
";
            comp.MarkupMatches(expectedHtml);
        }
    }
}

Let’s delve into this test case to learn more details about bUnit and xUnit:

  • First, the test class and test method must be public to be discovered by the xUnit test runner (lines # 7 & 10).
  • Second, the test method must have the [Fact] attribute on it to be discovered by the xUnit test runner (line #9). The Fact attribute validates that one case is true. There is another test definition attribute ([Theory]), which we will use in future articles.
  • Then, we start the test logic by creating the bUnit TestContext (line #13). The TestContext is a factory that makes it possible to create our components to test. It simulates the Blazor creation process for components and pages.
  • Next, we use the TestContext to create our BannerAd component by calling its RenderComponent method (line #16). That’s all it takes to render a simple component it bUnit. TestContext also supports more complex scenarios (like parameters, cascading values, and injected services), which we cover in additional articles.
  • We get back an IRenderedComponent for our component. It represents the rendered component and has methods to find elements within it, get the current markup, and perform semantic matching.
  • Then, we setup our expected HTML for the page (lines #19-32).
  • Finally. we call IRenderedComponent.MarkupMatches with our expected HTML (line #33). This method validates that the HTML in the render fragment is what we expected, using semantic matching – which means the HTML doesn’t need to be an exact string comparison match, but is equivalent to the HTML markup in IRenderedComponent. This makes validation a little more forgiving than directly using string comparison.
  • If the markup does not match, the MarkupMatches method fails an assertion and our test fails. Thanks to bUnit, we get a very descriptive message with which nodes failed comparison, and the actual and expected markup strings. This is great for debugging failing tests.

This gives us the basic structure of bUnit component tests. As we build more complex components, the tests will also get more complex. But this basic structure will be found in all of our bUnit tests.

Finally, let’s launch the new test in the Visual Studio TestExplorer and verify that it passes as we expect.

In conclusion, setting up a component test to use the bUnit TestContext is pretty easy. And, it provides us with the power to render the component in the same manner that Blazor will at runtime. It provides rich comparison helpers to assist us with validating the component state and markup. Finally, it integrates fully with the xUnit test runner and the Visual Studio Test Explorer to provide the common unit testing user experience that developers expect.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s