在BUnit Blazor单元测试中填充输入框并验证用户创建
Blazor注册页面单元测试:填充输入框与验证用户创建方案
针对你遇到的两个核心问题(InputText输入框无法填充、提交后无法验证用户创建),以下是具体解决方案:
一、解决InputText输入框填充问题
你的测试代码无法填充输入框,是因为操作的是外层.form-group容器,而非InputText内部的原生input元素,且Blazor的@bind-Value默认绑定onchange事件而非oninput。提供两种可行方式:
方式1:定位原生input触发Change事件
直接找到每个.form-group内的<input>标签,调用Change()方法传入测试值:
// 填充Display Name cut.Find(".form-group:has(label:contains('Display name')) input").Change("Zachrules123"); // 填充Email cut.Find(".form-group:has(label:contains('Email')) input").Change("Zachrules123@gmail.com"); // 填充Password cut.Find(".form-group:has(label:contains('Password')) input").Change("password"); // 填充重复密码 cut.Find(".form-group:has(label:contains('Re-enter password')) input").Change("password");
方式2:直接操作InputText组件
通过FindComponent<InputText>定位目标组件,设置Value并触发ValueChanged事件,适合复杂场景:
// 定位Display Name对应的InputText组件 var handleInput = cut.FindComponent<InputText>(x => x.Instance.ValueExpression.Body.ToString().Contains("_handle")); // 设置组件值并重新渲染 handleInput.SetParametersAndRender(p => p.Add(v => v.Value, "Zachrules123")); // 触发值变更事件同步到页面绑定字段 await handleInput.Instance.ValueChanged.InvokeAsync("Zachrules123");
二、验证用户是否成功创建
要替代人工查看数据库,需Mock数据库上下文(DbContext),通过验证Mock对象的方法调用来确认用户创建逻辑是否正确执行:
1. 优化注册页面的可测试性
首先确保页面通过构造函数注入DbContext,同时用强类型Model替代object作为EditForm的绑定模型(提升可维护性):
@page "/register" @inject AppDbContext Context <h2>Create account</h2> <EditForm Model="_registerModel" OnSubmit="@Submit"> <div class="form-group"> <label>Display name</label> <InputText @bind-Value="_registerModel.Handle" class="form-control" /> </div> <div class="form-group"> <label>Email</label> <InputText @bind-Value="_registerModel.Email" type="email" class="form-control" /> </div> <div class="form-group"> <label>Password</label> <InputText @bind-Value="_registerModel.Password" type="password" class="form-control" /> </div> <div class="form-group"> <label>Re-enter password</label> <InputText @bind-Value="_registerModel.RepeatPassword" type="password" class="form-control" /> </div> <div class="d-grid"> <button type="submit" class="btn btn-primary">Create your account</button> </div> </EditForm> <div> <a href="/signin">Sign in</a> </div> @code { private RegisterModel _registerModel = new(); private async Task Submit() { // 省略输入验证逻辑... var passwordHash = BCrypt.Net.BCrypt.HashPassword(_registerModel.Password); var user = new User { Id = Guid.NewGuid(), Username = _registerModel.Email.ToLowerInvariant(), Handle = _registerModel.Handle, Password = passwordHash, CreatedOn = DateTime.UtcNow // 填充其他必要字段 }; Context.Users.Add(user); await Context.SaveChangesAsync(); } public class RegisterModel { public string Handle { get; set; } public string Email { get; set; } public string Password { get; set; } public string RepeatPassword { get; set; } } }
2. 编写Mock DbContext的单元测试
使用Moq库Mock DbContext和DbSet,注入到测试上下文后验证方法调用:
using Bunit; using Moq; using Microsoft.EntityFrameworkCore; using YourApp.Data; using YourApp.Pages; namespace ZachUnitTests.Pages; public class RegisterTests { [Test] public async Task Submit_CreatesUserWithCorrectData() { // Arrange // Mock DbSet<User> var mockUsersSet = new Mock<DbSet<User>>(); // Mock AppDbContext var mockContext = new Mock<AppDbContext>(new DbContextOptions<AppDbContext>()); mockContext.Setup(c => c.Users).Returns(mockUsersSet.Object); using var ctx = new TestContext(); // 注入Mock的DbContext到测试容器 ctx.Services.AddScoped(_ => mockContext.Object); // Act var cut = ctx.RenderComponent<Register>(); // 填充输入框 cut.Find(".form-group:has(label:contains('Display name')) input").Change("Zachrules123"); cut.Find(".form-group:has(label:contains('Email')) input").Change("Zachrules123@gmail.com"); cut.Find(".form-group:has(label:contains('Password')) input").Change("password"); cut.Find(".form-group:has(label:contains('Re-enter password')) input").Change("password"); // 点击提交按钮 await cut.Find("button").ClickAsync(); // Assert // 验证是否添加了符合预期的User对象 mockUsersSet.Verify(set => set.Add(It.Is<User>(u => u.Handle == "Zachrules123" && u.Username == "zachrules123@gmail.com" && BCrypt.Net.BCrypt.Verify("password", u.Password) // 验证密码哈希正确性 )), Times.Once); // 验证是否调用了SaveChangesAsync mockContext.Verify(c => c.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once); } }
关键注意事项
- 必须通过依赖注入获取DbContext,禁止直接实例化,否则无法Mock数据库操作。
- 用强类型Model替代
object作为EditForm的Model,提升绑定效率与测试可维护性。 - 密码验证需使用哈希库的验证方法(如BCrypt.Verify),而非直接对比哈希字符串。
内容的提问来源于stack exchange,提问作者Zachary McArthur
相关产品推荐
相关产品推荐

