How-To: Write First bUnit Test

bUnit is a testing library for Blazor components. Its goal is to make it easy to write comprehensive, stable unit tests for these components. We will learn the steps to create our first bUnit test from setup to rendering to validation. This first test will be very simple to allow us to focus on the mechanics of creating the bUnit test, and not have to worry much about the test logic itself.

First, we must create a Blazor project and corresponding test project. 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.

Create New Test Class

To verify the default Index page, we are going create our test class in the Blazor.HowTo.Tests project by following these steps:

  1. Create a Pages 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 Pages folder, and select the ‘Add > Class’ menu item.
Fig 1 – Add New Item Dialog
  1. Name the class file: IndexTests.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 Test Method

Let’s implement the code for our first test to verify that the Index page renders the HTML we expect. Here is the code for the Index page:

@page "/"

<h1>Welcome to Blazor How-To!</h1>

<p>Welcome to your new app.</p>

Since it currently only has a welcome message in HTML and no logic, databinding, or interactivity, we only need one test to ensure we’re getting what we expect. So, here is the code for our test:

using Blazor.HowTo.Pages;
using Bunit;
using Xunit;

namespace Blazor.HowTo.Tests.Pages
{
    public class IndexTests
    {
        [Fact]
        public void InitiallRender()
        {
            // arrange
            using var ctx = new TestContext();

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

            // assert
            var expectedHtml =
@"<h1>Welcome to Blazor How-To!</h1>
<p>Welcome to your new app.</p>
";
            comp.MarkupMatches(expectedHtml);
        }
    }
}

Let’s dissect this test case to learn some 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). Fact validates that one case is true. There is another test definition attribute ([Theory]), which we will use in another article.
  • Then, we start 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/pages.
  • Next, we use the TestContext to create our Index page 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 other articles.
  • We get back an IRenderedComponent for our Index page. 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-22).
  • Finally. we call IRenderedComponent.MarkupMatches with our expected HTML (line #23). This method validates that the HTML 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 what is in IRenderedComponent. This makes validation a little more forgiving than string comparison.
  • If the markup does not match, the MarkupMatches method fails an assertion and our test fails. Luckily, 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 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.

Run the Test

Finally, let’s build our projects and run our tests to see the outcome:

  1. Build the solution by selecting the ‘Build > Build Solution’ item in the main menu.
  2. This should produce a successful build.
  3. Ensure that the Test Explorer tool window is open by selecting the ‘Test > Test Explorer’ item in the main menu.
  4. Click the ‘Run All’ button on the Test Explorer toolbar (the far left button).
  5. When the test completes in the Test Explorer, drill down into the ‘IndexTests > InitialRender> nodes and see the successful green indicator.
  6. The Test Detail Summary shows the test passed and how long it took to run. When there are errors in tests, this area contains helpful debugging information.
Fig 2 – InitialRender Test Run

That showed our test passing, but what does it look like on a failure? We will introduce a failure by changing the secondary welcome message in the <p> element. Then follow the steps above to build and run the test again.

Fig 3 – InitialRender Test Failure

This time we get a failure in the test list (shown by a red ‘X’). And when we look in the Test Detail Summary, we see a message telling us the HTML doesn’t match, which node is different, and the full layout of the actual and expected HTML.

In conclusion, setting up a test to use the bUnit TestContext is pretty easy. And, it provides us with the power to render a component/page 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.

Note: Although we are using xUnit to build and run our tests for this article, bUnit can be used in different test frameworks. In the past, I have also used it with the MsTest framework too.

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 )

Google photo

You are commenting using your Google 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