单元测试CreateWorkspaceHandler时Moq调用验证失败求助
单元测试验证数据库调用失败:预期调用一次但实际0次
Handler代码
.... namespace SchemaModeler.Host.Modules; ........ public class CreateWorkspaceHandler : IRequestHandler<CreateWorkspaceInput, Workspace?> { private readonly IDbContextFactory<AppDbContext> _dbContextFactory; private readonly ITopicEventSender _eventSender; public async ValueTask<Workspace?> Handle( CreateWorkspaceInput request, CancellationToken cancellationToken) { await using AppDbContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); if (string.IsNullOrEmpty(request.Name)) { throw new InvalidNameException(request.Name); } Organization organization = await dbContext.Organizations .FirstOrDefaultAsync(x => x.Id == request.OrganizationId, cancellationToken) ?? throw new EntityNotFoundException(request.OrganizationId, nameof(Organization)); ................. var workspace = new Workspace { ....... Organization = organization, ........ }; ....... await _eventSender.SendAsync(nameof(WorkspaceSubscriptions.OnCreatedWorkspace), workspace, cancellationToken); return workspace; } }
测试代码
.... namespace SchemaModeler.Host.Tests.UnitTests; .... [UsesVerify] [Collection("Postgres")] public class WorkspaceTests : TestBase { public override async Task InitializeAsync() { await base.InitializeAsync(); // Call the base InitializeAsync() method await using AppDbContext context = CreateDbContext(); context.Workspaces.Add(WorkspaceMockFixture.Workspaces); await context.SaveChangesAsync(); } [Fact] public async Task Handle_InvalidName_ThrowsInvalidNameException() { var organizationId = new Guid(); ......... var mockOrganizationsDbSet = new Mock<DbSet<Organization>>(); mockOrganizationsDbSet.Setup(m => m.FindAsync(It.IsAny<object[]>())) .ReturnsAsync((Organization)null); MockDbContext.Setup(dbContext => dbContext.Set<Organization>()) .Returns(mockOrganizationsDbSet.Object); .......... MockDbContext.Verify( dbContext => dbContext.Set<Organization>().FindAsync( It.IsAny<object[]>(), CancellationToken ), Times.Once ); // 此处报错 .......... } }
运行测试抛出的异常
Moq.MockException : Expected invocation on the mock once, but was 0
times: dbContext => dbContext.Set< Organization >().FindAsync(It.IsAny<object[]>(),
CancellationToken)Performed invocations:
MockAppDbContext:1 (dbContext): No invocations performed.
问题分析与解决思路
- 执行流程逻辑冲突:Handler中优先检查
request.Name是否为空,一旦为空直接抛出InvalidNameException,后续查询Organization的代码根本不会执行。当前测试用例的场景是验证无效名称抛出异常,此时数据库查询逻辑未触发,所以Verify断言必然失败。 - 错误的验证方向:该测试用例的核心目标是验证
InvalidNameException的抛出,无需验证数据库调用。应删除Verify代码,替换为异常断言,例如:await Assert.ThrowsAsync<InvalidNameException>(async () => await handler.Handle(new CreateWorkspaceInput { Name = null, OrganizationId = organizationId }, CancellationToken.None) ); - 方法不匹配问题:即使走到数据库查询逻辑,Handler中使用的是
FirstOrDefaultAsync,但测试中Verify的是FindAsync,两者方法不一致,断言仍会失败。需保持方法匹配:要么修改Handler使用FindAsync,要么调整测试VerifyFirstOrDefaultAsync的调用。 - 拆分测试场景:若要验证数据库调用,需单独编写测试用例,传入有效的
Name参数,确保代码走到查询Organization的逻辑分支。
内容的提问来源于stack exchange,提问作者kumar425
相关产品推荐
相关产品推荐

