htmlunit-testingblazorradzenbunit

How to write unit tests for Blazor using bUnit when Radzen components generate random id?


I'm using Radzen in my Blazor project and I decided to learn to write tests with bUnit. However, I'm encountering difficulties.

Sometimes a random id is assigned to elements generated by Radzen components. This results in a mismatch when I compare the result of the test with what I expect it to be.

For example, I have a component called DefaultProcessingTemplate.razor:

@using ClientProcessing.Library
@inject IJSRuntime JsRuntime
//@inject DialogService DialogService

@if (ClientProcessor as IDataProcessing is not null)
{
    <RadzenRow>
        <RadzenHeading Size="H2" Text="Reporting"></RadzenHeading>
    </RadzenRow>
    <RadzenRow Gap=".5rem" class="rz-mb-6">
        <RadzenButton Click="OpenReportingRunDialog" ButtonStyle="ButtonStyle.Primary" Text="Run"></RadzenButton>
        <RadzenButton Click="OpenReportingDownloadDialog" ButtonStyle="ButtonStyle.Primary" Text="Download"></RadzenButton>
    </RadzenRow>
}
else
{
    // code ...
}

@code {
    private DialogOptions defaultDialogOptions = new DialogOptions { CloseDialogOnEsc = true, Width = "900px" };

    [Parameter]
    public IDataProcessing ClientProcessor { get; set; }

    protected override void OnInitialized()
    {
        // ... code
    }

    private async Task OpenReportingRunDialog()
    {
        var parameters = new Dictionary<string, object>()
        {
            { "CommDate", ClientProcessor.GetCurrMo() },
            { "Reporting", ClientProcessor }
        };
        //await DialogService.OpenAsync<ReportingRun>("Run", parameters, defaultDialogOptions);
    }

    private async Task OpenReportingDownloadDialog()
    {
        var reporting = ClientProcessor as IReporting;
        var parameters = new Dictionary<string, object> { { "FilePaths", reporting.GetReportPaths() } };

        var options = new DialogOptions
            {
                CloseDialogOnEsc = true,
                Width = "1200px"
            };

        //await DialogService.OpenAsync<DownloadFiles>("Download", parameters, options);
    }
}

I want to test that if the ClientProcessor parameter is not null, DefaultProcessingTemplate component contains the <RadzenHeading Size="H2" Text="Processing"></RadzenHeading> element. The following is the best I have come up with to test this.

DefaultProcessingTemplateTests.razor:

@using ClientProcessing.BlazorServer.Components.Shared
@using ClientProcessing.Library
@using Moq
@using Radzen.Blazor

@inherits TestContext
@code {
    private readonly Mock<IDataProcessing> _mockDatabaseProcessing = new Mock<IDataProcessing>();

    [Fact]
    public void HasSection()
    {     
        var cut = Render(@<DefaultProcessingTemplate ClientProcessor="_mockDatabaseProcessing.Object" />);

        cut.Find("h2").MarkupMatches(@<RadzenHeading Size="H2" Text="Reporting"></RadzenHeading>);
    }
}

As a result of those randomly generated ids, the test fails:

Message: 
Bunit.HtmlEqualException : HTML comparison failed. 

The following errors were found:
  1: The values of the attributes at h2(0)[id] are different.

Actual HTML: 
<h2 class="rz-heading" id="4KjgjHwknU" >Reporting</h2>

Expected HTML: 
<h2 class="rz-heading" id="cHelqcQDgU" >Reporting</h2>

How can I write tests for Blazor components that use Radzen? Is there a convenient way to do this?


Solution

  • Bunit allows you to customize how markup is compared, see https://bunit.dev/docs/verification/semantic-html-comparison.html

    However, in this case you are essentially testing Radzons components which I don't recommend, tightly coupling your test to a Radzons implementation detail (the markup their component produces) instead of their public api surface.

    What you probably want to test here is that there is an expected child component (RadzenHeading) of your component under test, and that the expected parameters was passed to it, e.g.:

    var rh = cut.FindComponent<RadzenHeading>();
    Assert.Equal("H2", rh.Instance.Size);
    Assert.Equal("Heading", rh.Instance.Text);