In this guide to unit testing Blazor components, we'll cover everything from setting up your projects to simple unit test examples and a master-detail scenario.
Blazor is a hot topic these days, and this is why I decided to experiment on what is possible at this early stage in regard to unit testing. To my surprise there is already available library named bUnit for rendering the components under test. And JustMock Lite along with JustMock were perfectly capable of mocking everything I tried.
In this post I will show you some simple examples that may be of interest to you. First, I will start with how to set up your projects, continue with simple unit test examples and finish with a master-detail scenario.
Let's begin setting up your projects. As with any other technology you will need at least two projects. The first one is the Blazor application and the second one is for the unit tests.
Creating your Blazor project should not be a problem as there is a template in Visual Studio 2019.
The newly created project contains default pages and data classes that we will slightly extend and use for the examples.
Setting up the unit test project is not as straightforward as the Blazor application. Just follow the steps below and you should be fine.
This is because the Razor Class Library project is targeting .NET standard. Change it to target .NET core app 3.1. The result in the project file should be the following:
And again, try to add the bUnit NuGet package.
OK, we have created the test project and we successfully built it. Now we need to prepare out test class.
Now we are ready to start writing our first test.
If you start the Blazor Demo application that we have added, you will notice that in the left navigation there are three links. Home, Counter and Fetch Data. I will test the functionality in the Counter page where clicking a button will increase the counter.
To do this I will have to first render the Counter page. Find where the counter value is located. Validate that this is zero. Click on the button and validate that the counter value has changed to 1. Here is what this test will look like:
In the default Blazor application there is a page named FetchData which uses the WeatherForecastService to fetch the data and show it. I want to show how useful it is to use bUnit to obtain certain components and this is why I will move the part with representing the weather forecast data in a separate component. Here is how the code looks:
And here is how the ForecastDataTable component looks like:
Now I can start writing the tests. For this particular page I need to write two unit tests. The first should test that the loading text is shown when the forecasts variable is null. And the second one is to test that the data is shown correctly when there is actual data.
The first step for testing this scenario is registering the WeatherForecastService.Services.AddSingleton
After that I will need to render the FetchData page and validate that the loading text is shown. Here is how the unit test looks like:
If you execute this test now you will notice that it will fail. The reason for this is because the WeatherForecastService generates random values and the forecasts variable in the component will never be null.
This is a good candidate for mocking. If you are not familiar with the concept, in mocking, to test the required logic in isolation the external dependencies are replaced by closely controlled replacements objects that simulate the behavior of the real ones. For our scenario the external dependency is the call to the WeatherForecastService.GetForecastAsync method.
To mock this method, I will have to use a mocking framework like Telerik JustMock. It is capable of mocking literally everything - from public to internal, private or static API, even members of MsCorLib like DateTime.Now and more.
There is a free version of JustMock named JustMock Lite. JustMock Lite has the limitation to be able to mock only public virtual methods and public interfaces. You could check this comparison table between JustMock and JustMock Lite to see the difference.
Back to the code. For this blog post I decided to use JustMock Lite and because of this I will need to modify the WeatherForecastService to inherit an interface and work with that interface instead. Here is what this will look like:
To work with this interface instead of the actual implementation several changes should be made. First the WeatherForecastService should inherit this interface.
Next, the FetchData page should use it. Here is the chunk of code that should be modified:
Also, for the BlazorDemo App to continue working I need to modify the Startup.ConfigureServices method and add IWeatherForecastService as a singleton service with implementation WeatherForecastService. Like this:
Now we are ready to create the mock.
What I will do is create a mock of type IWeatherForecastService and arrange the GetForecastAsync method for any DateTime argument to return a value that will result in null value for the forecast variable. At the end the mocked instance should be registered as implementation of our interface. Here is how the whole test looks:
For this scenario what I will do is create a mock of the GetForecastAsync similarly to what I did in the previous test, but this time the method will return a single predefined value. I will use this value later for validation.
Next I will register the IWeatherForecastService with the implementation of the created mock. After that I will render the FetchData component. bUnit has an API that allows me to search for a nested component in another component. This is what I will do as I have already extracted the forecast data representation in another component. At the end I will compare the actual result with expected value. Here is what this unit test will look like:
The last scenario that I wanted to show is how to test a master-detail. To save time in development of this case I will use the Teleik Blazor Grid, as it has built-in master-detail support. To be able to run the scenario you should download the Telerik Blazor trial if you don’t already have a license. For more information you could read the What You Need to Use the Telerik Blazor Components documentation article.
As a first step I must create a new page. I will use a MasterDetail name for this. Here is how the code of this page looks:
As you can see it is the same as the previous one, with the only difference that it is using the ForecastDataGrid component. And here is what the code of the ForecastDataGrid component looks like:
And here is what the code of the WeatherForecastDetail component looks like:
If you paste this code and build it, you will notice an error stating that the Telerik Grid can’t be found. To solve this add the Telerik.Blazor and Telerik.Blazor.Components usings to the _Imports.razor file.
As a next step I must add the TelerikRootComponent in the MainLayout page. This is required to allow the Telerik Blazor Grid to work with detached popups as it was explained in the documentation article I've provided above. Here is how the MainLayout should look:
I have created the master detail page, and the build is passing. Now I need to add the new page to the navigation. To do so, I need to modify the NavMenu page and add the following:
Another thing that needs to be done is the registration of the Telerik Blazor service in the Startup.ConfigureServices method. Here is how the new code will look like:
The result is that in my BlazorDemo application I have a master detail page. Here is an image of the final result:
What I want to verify is that the master detail is working as expected. For that purpose I want to make the unit test render the page, click on the plus sign and verify that the detail component is rendered with the correct data.
To achieve this I will again create a mock of IWeatherForecastService and arrange it to return a predefined single value which will be used later for comparison. For this case a mock of JsRuntime should be registered as a service. bUnit provides such mock out of the box and can be used by simply adding Services.AddMockJsRuntime(); I must register the TeleirkBlazor service as well.
As the Telerik Grid has a requirement for the root element to be TelerikRootComponent, I will create a mock of TelerikRootComponent and render the grid as nested component. Here is how:
After the rendering is done I will have to find the plus sign and click on it. Here is how:
Next I will make a separate rendering of the WeatherForecastDetail component with the predefined values. This rendering will be used as the expected value in the comparison. Here is how:
And lastly, I will have to find the actual detail component and compare it to the expected one. Here is how:
And here is what the whole test looks like:
You can find the code used for the examples in this github repo.
If you are interested in the topic and you want to see markup tests or more complex scenarios, please comment in the section bellow.
Mihail Vladov is a Software Engineering Manager at Progress. He has more than a decade of experience with software and product development and is passionate about good software design and quality code. Mihail helped develop the WPF controls suite and Document Processing libraries which are used by thousands of developers. Currently, he is leading the JustMock team. In his free time, he loves to travel and taste different foods. You can find Mihail on LinkedIn.
Subscribe to be the first to get our expert-written articles and tutorials for developers!