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
相关产品推荐
相关产品推荐

