Unit testing is a level of software testing that is used by developers to ensure that the implemented logic meets its requirements and behaves as expected.
bUnit is a testing library for Blazor components used to write comprehensive and stable unit test cases. It allows you to write test cases in C# or Razor syntax to verify outcomes with semantic HTML diffing and comparison logic. With it, you can interact with components, event handlers, and cascading values; inject services; mock IJSRuntime; and take snapshots.
bUnit is not a unit test runner, but it is built on top of traditional unit testing frameworks such as xUnit, NUnit, and MSTest.
Now, let’s learn how to write a test in C# for Blazor components!
Follow these steps to create a Blazor application:
The Blazor app will be created.
Follow these steps to create a project for testing Blazor components:
Now, we are going to write a test case for a Razor component.
[SetUp] public void Setup() { } [Test] public void HelloSyncfusionComponentTest() { // Arrange using var context = new Bunit.TestContext(); string expectedMarkup = "<h3>HelloSyncfusion</h3>"; // Act var cut = context.RenderComponent<BlazorbUnit.HelloSyncfusion>(); // Assert cut.MarkupMatches(expectedMarkup); }
In this code:
using var context = new Bunit.TestContext(); creates an instance of the bunit.TestContext class. Here, we are using the using var syntax to avoid unnecessary indention.
string expectedHTML = “<h3>HelloSyncfusion</h3>”; is Blazor component markup.
var cut = context.RenderComponent<BlazorbUnit.HelloSyncfusion>(); renders the Blazor component using TestContext.
cut.MarkupMatches(expectedHTML); verifies the rendered markup from the component using the MarkupMatches method. This performs a semantic comparison of the expected markup with the rendered markup.
Note: This is a component rendered test case. So, if the test case passes, the component will be rendered successfully.
Follow this procedure to create a test case for an event handler:
@page "/HelloSyncfusion" <h3>HelloSyncfusion</h3> <button class="btn btn-primary" @>In the above code, we have written a test case for the @onclick=”ClickMeToSayHi” event. After the button is clicked, a message will be shown below it. This operation is handled in this event.
[Test] public void SayHiByClick() { // Arrange using var context = new Bunit.TestContext(); // Act var cut = Context.RenderComponent<BlazorbUnit.HelloSyncfusion>(); cut.Find("button").Click(); // Assert cut.Find("h5").MarkupMatches("<h5>Hi Syncfusion, Nice to meet you :)</h5>"); }
From the test method, var cut = Context.RenderComponent<BlazorbUnit.HelloSyncfusion>(); renders the Blazor component.
Then, using the Find method, the button is located using the component tag, class, or ID, and the click event is triggered by using the code
cut.Find(“button”)[0].Click();
This triggers the click event and shows the message. Then, find the message markup and compare it with the expected markup using following code.
cut.Find("h5").MarkupMatches("<h5>Hi Syncfusion, Nice to meet you :)</h5>");
Note: Before triggering the click event, the message is hidden. So, we cannot get the message’s markup.
The Click event test case has passed.
bUnit has plenty of ways to pass parameters. Here, I am going to explain non-Blazor type parameters.
using static Bunit.ComponentParameterFactory;
<button class="btn btn-primary" @>In the above code, we have set the property for the parameter and cascading parameter.
[Parameter] public int Parameter { get; set; } [CascadingParameter] public int CascadingParameter { get; set; }Both properties are going to catch the parameter and cascading parameter when it is passed.The parameter and cascading parameter values are shown by clicking button. The @onclick=”ClickToShowParameters” event is going to perform this operation.
[Test] public void PassingParameters() { // Arrange using var context = new Bunit.TestContext(); int parameter = 1; int cascadingParameter = 2; // Act var cut = Context.RenderComponent<BlazorbUnit.HelloSyncfusion>(("Parameter", parameter), CascadingValue(cascadingParameter)); cut.FindAll("button")[1].Click(); // Assert cut.Find(".parameter").MarkupMatches("<div class='parameter'>Parameter: 1</div>"); cut.Find(".cascadingParameter").MarkupMatches("<div class='cascadingParameter'>Cascading Parameter: 2</td>"); }
In this code,
var cut = context.RenderComponent<BlazorbUnit.HelloSyncfusion>((“Parameter”, parameter), CascadingValue(cascadingParameter)); is the way to pass a parameter. Here, “Parameter” is the property name of the parameter initialized in the Blazor component.
Find the button using the FindAll method and trigger the click event cut.FindAll(“button”)[1].Click();. Passed values will be shown in the Razor component, and then we can match the markup using following code.
cut.Find(".parameter").MarkupMatches("<div class='parameter'>Parameter: 1</div>"); cut.Find(".cascadingParameter").MarkupMatches("<div class='cascadingParameter'>Cascading Parameter: 2</td>");
Here, we are going to configure a bUnit instance into a setup method. Once configured into the setup, you don’t need to create a new instance in every test method. Instead, you can use the common instance. Then add Context.Dispose(); code into TearDown method to dispose context. Context will be disposed once each test run.
Here, bUnit TestContext instance code was removed in each test method and instantiated in the setup method.
Injecting a service is essential if you are using a service in your Blazor component. Let me explain how to inject services in a test method:
Context.Services.AddSingleton<BlazorbUnit.SyncfusionService>(new BlazorbUnit.SyncfusionService());
A mock object allows copying or cloning the behavior of a class and interface. This is used when interacting with test cases.
Follow these steps to mock IJSRuntime:
We can also mock other objects as needed.
bUnit is built on a testing framework. So, there is no need to do separate configuration in an Azure Pipeline. If you have configured NUnit in an Azure pipeline, that is more than enough to put bUnit test cases into it.
Refer to this blog to learn how to configure an Azure Pipeline:
https://www.syncfusion.com/blogs/post/easy-steps-to-integrate-azure-pipelines.aspx
In this blog post, we discussed the steps to create bUnit tests for Blazor components and how to integrate them in an Azure Pipeline. Try out these steps for yourself and leave your feedback in the comments section below.
For more details refer, https://bUnit.egilhansen.com
Try our 65+ Blazor UI components by downloading the free 30-day trial or NuGet package, and feel free to have a look at our documentation, GitHub, and online examples to explore other available features.
You can also contact us through our support forum, Direct-Trac, or feedback portal. As always, we would like to hear from you!