loading...

DevOps Blazor WebAssembly Solution: Part 2 - Add more complex unit tests

kenakamu profile image Kenichiro Nakamura ・3 min read

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.
Alt Text

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.
Alt Text

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);
        }      
    }
}

2. Compile and run the test.
Alt Text

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)
);

3. Compile and run the test.
Alt Text

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.
Alt Text

Summary

I completed all components by following Steve's blog. In the next article, I use Azure DevOps to CI/CD the solution.

Go to next article

Discussion

pic
Editor guide