You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

bUnit WaitForAssertion多渲染周期提前通过,如何等待最终状态?

Blazor组件bUnit测试:确保等待最终渲染状态验证多错误提示

我有一个Blazor组件,会对上传文件执行多项验证。当两项验证均失败时,应显示两个独立的警告框。但在bUnit测试中,模拟两项验证都失败后,Assert.Equal(1, alerts.Count())居然通过了(预期应该是2,测试本应失败)。看起来WaitForAssertion在第一个错误渲染完成后就结束了,没有等待逻辑执行完毕。

后台逻辑

private void AddFilesToQueue(InputFileChangeEventArgs e)
{
    try
    {
        var files = e.GetMultipleFiles();
        foreach (var file in files)
        {
            if (!fileValidator.IsSizeValid(file.Size, ...)) {
                filesOverMaxSize.Add(file);
            }
            if (!fileValidator.IsTypeValid(file.Name, ...)) {
                filesWrongType.Add(file);
            }
        }

        if (filesOverMaxSize.Count > 0)
        {
            foreach (var file in filesOverMaxSize)
                FileSizeErrors?.Add($"Size error for {file.Name}");
        }

        if (filesWrongType.Count > 0)
        {
            foreach (var file in filesWrongType)
                FileTypeErrors?.Add($"Type error for {file.Name}");
        }
        StateHasChanged();
    }
    catch (InvalidOperationException ex) { ErrorMessage = ex.Message; }
}

标记代码

@if (!string.IsNullOrEmpty(ErrorMessage))
{
    <div class="alert">@ErrorMessage</div>
}
@foreach (string message in FileSizeErrors ?? new())
{
    <div class="alert">@message</div>
}
@foreach (string message in FileTypeErrors ?? new())
{
    <div class="alert">@message</div>
}

测试代码

[Fact]
public void SingleErrorShown_WhenSingleFileHasMultipleFailures()
{
    AddInjectedServices();
    var serviceMock = new Mock<IFileValidator<FilingSettingsClient>>();
    serviceMock.SetupProperty(m => m.FilingSettings, new FilingSettingsClient());

    // Mock BOTH to fail
    serviceMock.Setup(m => m.IsSizeValid(It.IsAny<long>(), It.IsAny<long>())).Returns(false);
    serviceMock.Setup(m => m.IsTypeValid(It.IsAny<string>(), It.IsAny<string[]>())).Returns(false);

    Services.AddSingleton(serviceMock.Object);
    var component = Render<FileUploader>();

    var fileToUpload = InputFileContent.CreateFromBinary(new byte[100], "invalid-type.exe");

    // Act
    component.FindComponent<InputFile>().UploadFiles(fileToUpload);

    // Assert - This SHOULD fail because 2 alerts should exist, but it PASSES.
    component.WaitForAssertion(() =>
    {
        var alerts = component.FindAll(".alert");
        Assert.Equal(1, alerts.Count()); 
    }, TimeSpan.FromSeconds(3));

    serviceMock.Verify(m => m.IsTypeValid(It.IsAny<string>(), It.IsAny<string[]>()), Times.AtLeastOnce());
}

观察结果

  • Verify确认两个验证方法均被调用。
  • 若将断言改为Assert.Equal(2, alerts.Count()),测试也会通过,说明第二个警告框最终会出现。
  • 似乎WaitForAssertion在第一个if块触发渲染后就退出,忽略了后续的状态变更。

解决方法

1. 直接断言最终期望状态

WaitForAssertion的特性是断言第一次成立时就立即返回,所以如果你的断言是检查数量等于1,它会在第一个警告框渲染完成后就通过测试。正确的做法是直接断言最终期望的数量(2),让WaitForAssertion等待直到条件满足:

component.WaitForAssertion(() =>
{
    var alerts = component.FindAll(".alert");
    Assert.Equal(2, alerts.Count); 
}, TimeSpan.FromSeconds(3));

2. 使用WaitForState等待最终状态

如果需要更明确地等待状态达成,可以用WaitForState先确认状态满足,再执行断言:

// 等待直到警告框数量变为2
component.WaitForState(() => component.FindAll(".alert").Count == 2, TimeSpan.FromSeconds(3));
// 执行最终断言
Assert.Equal(2, component.FindAll(".alert").Count);

3. 确保集合变更能触发渲染(可选)

如果FileSizeErrors和FileTypeErrors是普通List<string>,虽然最后调用了StateHasChanged()能触发渲染,但改用ObservableCollection<string>可以让Blazor自动感知集合元素的添加,避免因集合类型导致的渲染延迟问题。

内容的提问来源于stack exchange,提问作者yummybuns

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 17:43:12