As a C# developer, I love Blazor, but there are several things to consider.
- WebAssembly is still in preview
- No official unit test framework is supported (yet)
However, unit testing is essential to DevOps. Fortunately, Steve Sanderson published Unit testing Blazor components - a prototype back in Aug last year, so I use this to write unit test and do DevOps Blazor WebAssembly.
Blazor WebAssembly project
Follow the instruction here to create Blazor WebAssembly project. I believe you can do server side Blazor but I stick with WebAssembly this time :)
1. Install Microsoft.AspNetCore.Components.WebAssembly.Templates and create new project. Select Blazor App.
2. Select WebAssembly App. I decided to host this in ASP.NET Core.
3. Compile and the run the application to make sure it works fine.
Git clone testing repository
To use the testing library, I need to git clone as it's not published as NuGet.
git clone https://github.com/SteveSandersonMS/BlazorUnitTestingPrototype
Copy Microsoft.AspNetCore.Components.Testing to my project.
Add unit test project
The Testing prototype works with Unit Test project such as xUnit.
1. Add C# .NET Core xUnit Test project to the solution. I named it as "DevOpsBlazor.UnitTests".
2. Then add existing project by selecting Microsoft.AspNetCore.Components.Testing.csproj.
3. Finally, Add project references to xUnit project. As the server project has references to client and shared, I just added server and testing project.
4. Optionally, update all NuGet and compile entire solution to make sure there is no errors.
Add Unit Tests
The out of box application has several Blazor components. Let's start adding first unit test. Index.razor is the default page of the application. It just contains static information, so it should be good starting point.
Assert HTML Element
TestHost is a class which renders Blazor components like a browser. It provides several methods such as Find, FindAll to find HTML elements or GetMarkup to get generated HTML as string.
1. Rename existing UnitTest1.cs to IndexPageUnitTests.cs and change existing method name to "ShouldRenderHello".
using System;
using Xunit;
namespace DevOpsBlazor.UnitTests
{
public class IndexPageUnitTests
{
[Fact]
public void ShouldRenderHello()
{
}
}
}
2. Add TestHost class property and solve using. The Blazor component will be rendered into this TestHost class and I can use it to assert various things.
TestHost host = new TestHost();
3. In ShouldRenderHello method, render the Index page. Now the component instance contains rendered results of Index.
var component = host.AddComponent<Client.Pages.Index>();
4. Let's assert h1 tag message. The Find method takes CSS selector and return first found element.
Assert.Equal("Hello, world!", component.Find("h1").InnerText);
5. Compile the solution, then run the test in Test Explorer.
Assert Child Component
Index.razor contains SurveyPrompt.razor as child component. Let's see how child components are rendered inside TestHost.
1. Put breakpoint at the Assert statement and run the test in debug mode.
2. See the result of component.GetMarkup() in Watch window.
3. I see child component is also rendered as pure HTML. Stop the debug and replace current assert.
// Assert h1
Assert.Equal("Hello, world!", component.Find("h1").InnerText);
// Assert text in the body
Assert.Contains("Welcome to your new app.", component.GetMarkup());
// Assert if survey component exists
Assert.NotNull(component.Find(".alert.alert-secondary"));
// Assert the link
Assert.Equal(
"https://go.microsoft.com/fwlink/?linkid=2127212",
component.Find("a").Attributes["href"].Value);
Work with parameters
Though I already test SurveyPrompt as part of Index, I should test this separately as it takes a parameter.
1. Add SurveyPromptUnitTests.cs to test project. Then replace the code as following.
using Microsoft.AspNetCore.Components.Testing;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace DevOpsBlazor.UnitTests
{
public class SurveyPromptUnitTests
{
TestHost host = new TestHost();
[Fact]
public void ShouldRenderTitle()
{
}
}
}
2. Render the component by passing Title parameter.
var title = "Test";
var parameters = new Dictionary<string, object>() { { "Title", title } };
var component = host.AddComponent<SurveyPrompt>(parameters);
Assert.Equal(title, component.Find("strong").InnerText);
3. Compile solution and run the test.
Summary
In this article, I simply added several unit tests to try using TestHost. In the next article, I will add more test which covers more scenarios.
Additional Information
I used BlazorUnitTestingPrototype this time, but there is another interesting unit test project called bunit. I will use this in the future too.
Top comments (1)
I am not able to run the tests shown in this demo because i am getting this exception error "System.MissingFieldException : Field not found: 'Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame.FrameType'."
I have configured everything as described in the demo and i am using .Net 7. Could that be a problem ? Could you please share the source for this demo ?
Thank you for your feedback.