In the previous article, I added basic unit test to a Blazor WebAssembly project which I created from Visual Studio template.
In this article, I complete writing unit tests so that I can move to DevOps part in the next article.
Test component with private method
Counter.razor has IncrementCount private method as well as currentCount private property. When razor page is compiled, it compiled as just another C# class. However, all the methods in razor page should be UI related logic, I won't test each method alone. Let's add unit test for Counter.razor.
Test initial state
1. Add CounterPageUnitTests.cs and replace the code.
using DevOpsBlazor.Client.Pages;
using Microsoft.AspNetCore.Components.Testing;
using System;
using Xunit;
namespace DevOpsBlazor.UnitTests
{
public class CounterPageUnitTests
{
TestHost host = new TestHost();
[Fact]
public void ShouldRenderCountZero()
{
var component = host.AddComponent<Counter>();
// Assert p
Assert.Equal("Current count: 0", component.Find("p").InnerText);
}
}
}
2. Compile and run the test. This is as simple as previous article.
Test button click
I will test button click and assert the result now.
1. As button click event is async, I use async/await. Add new Fact and solve dependency.
[Fact]
public async Task ShouldIncrementCount()
{
}
2. Add button click event and assert the result.
var component = host.AddComponent<Counter>();
// Find a button and click.
await component.Find("button").ClickAsync();
// Assert result.
Assert.Equal("Current count: 1", component.Find("p").InnerText);
3. Compile the solution and run the test.
Dependency Injection
FetchData.razor has injection of HttpClient and call backend server. Let's see how I can mock the service. The TestHost provides AddMockHttp method to mock the service.
Test the initial state
Let's start writing basic test.
1. Add FetchDataPageUnitTests.cs and update the code.
using DevOpsBlazor.Client.Pages;
using Microsoft.AspNetCore.Components.Testing;
using System;
using Xunit;
namespace DevOpsBlazor.UnitTests
{
public class FetchDataPageUnitTests
{
TestHost host = new TestHost();
[Fact]
public void ShouldRenderLoading()
{
var req = host.AddMockHttp().Capture("");
var component = host.AddComponent<FetchData>();
// Assert p
Assert.Equal("Loading...", component.Find("p em").InnerText);
}
}
}
Test HttpCall
Let's add separate test for loading data.
1. Add ShouldRenderFocusts method and add following code. To solve WeatherForecast most, add using too. req.SetResult returns dummy data and host.WaitForNextRender will render the page again once data is set.
[Fact]
public void ShouldRenderFocusts()
{
var req = host.AddMockHttp().Capture("");
var component = host.AddComponent<FetchData>();
host.WaitForNextRender(() => req.SetResult(new[]
{
new WeatherForecast { Summary = "First", TemperatureC = 30, Date = DateTime.Now.AddDays(1) },
new WeatherForecast { Summary = "Second", TemperatureC = 28, Date = DateTime.Now.AddDays(2)},
}));
}
2. Once data is loaded, I can assert entire table using Assert.Collection.
Assert.Collection(
component.FindAll("tbody tr"),
row => Assert.Contains("First", row.OuterHtml),
row => Assert.Contains("Second", row.OuterHtml)
);
4. I got above code from Steve's blog but let's make it a bit more granular. It uses Html Agility Pack to parse the HTML, so replace the assert with following code. You can assert whatever you want.
Assert.Collection(
component.FindAll("tbody tr"),
row =>
{
Assert.Equal(DateTime.Now.AddDays(1).ToShortDateString(), row.SelectSingleNode("td[1]").InnerText);
Assert.Equal("30", row.SelectSingleNode("td[2]").InnerText);
Assert.Equal((32 + (int)(30 / 0.5556)).ToString(), row.SelectSingleNode("td[3]").InnerText);
Assert.Equal("First", row.SelectSingleNode("td[4]").InnerText);
},
row => {
Assert.Equal(DateTime.Now.AddDays(2).ToShortDateString(), row.SelectSingleNode("td[1]").InnerText);
Assert.Equal("28", row.SelectSingleNode("td[2]").InnerText);
Assert.Equal((32 + (int)(28 / 0.5556)).ToString(), row.SelectSingleNode("td[3]").InnerText);
Assert.Equal("Second", row.SelectSingleNode("td[4]").InnerText);
}
);
5. Make sure test still runs successfully.
Summary
I completed all components by following Steve's blog. In the next article, I use Azure DevOps to CI/CD the solution.
Top comments (0)