如何在C# XUnit中验证非Mock方法是否被调用
验证
player.ConfirmPlayer()调用的测试方案 因为Player对象是从EF Core的DbSet中查询得到的,没有被注入或Mock,无法直接用Moq验证方法调用,以下是三种可行的测试方案:
方案一:验证Player的状态变更(推荐,符合行为驱动测试逻辑)
ConfirmPlayer()的核心作用是将玩家标记为已确认状态,与其验证方法是否被调用,不如直接验证调用后的业务状态是否符合预期——这更贴合测试的实际意义。
测试步骤示例(xUnit + Moq):
[Fact] public async Task Handle_ShouldMarkPlayerAsConfirmed() { // 准备测试数据 var testPlayerId = Guid.NewGuid(); var testPlayer = new Player { Id = testPlayerId, IsConfirmed = false }; var mockDbSet = new Mock<DbSet<Player>>(); mockDbSet.Setup(m => m.FirstAsync(It.IsAny<Expression<Func<Player, bool>>>(), default)) .ReturnsAsync(testPlayer); var mockContext = new Mock<YourDbContext>(); mockContext.Setup(c => c.Players).Returns(mockDbSet.Object); var handler = new ConfirmPlayerCommandHandler(mockContext.Object); var request = new ConfirmPlayerCommand { PlayerId = testPlayerId }; // 执行目标方法 await handler.Handle(request, CancellationToken.None); // 验证状态变更(间接证明ConfirmPlayer被调用) Assert.True(testPlayer.IsConfirmed); mockContext.Verify(c => c.SaveChangesAsync(default), Times.Once); }
方案二:创建可Mock的Player子类(针对必须验证方法调用的场景)
如果业务要求必须严格验证ConfirmPlayer()方法的调用行为,可以修改Player类的方法可继承性,通过Mock子类来实现验证:
- 先修改
Player类,将ConfirmPlayer()标记为virtual:
public class Player { public Guid Id { get; set; } public bool IsConfirmed { get; private set; } public virtual void ConfirmPlayer() { IsConfirmed = true; } }
- 编写测试代码验证方法调用:
[Fact] public async Task Handle_ShouldCallConfirmPlayer() { // 准备Mock的Player实例 var testPlayerId = Guid.NewGuid(); var mockPlayer = new Mock<Player>(); mockPlayer.Setup(p => p.Id).Returns(testPlayerId); var mockDbSet = new Mock<DbSet<Player>>(); mockDbSet.Setup(m => m.FirstAsync(It.IsAny<Expression<Func<Player, bool>>>(), default)) .ReturnsAsync(mockPlayer.Object); var mockContext = new Mock<YourDbContext>(); mockContext.Setup(c => c.Players).Returns(mockDbSet.Object); var handler = new ConfirmPlayerCommandHandler(mockContext.Object); var request = new ConfirmPlayerCommand { PlayerId = testPlayerId }; // 执行目标方法 await handler.Handle(request, CancellationToken.None); // 直接验证方法调用 mockPlayer.Verify(p => p.ConfirmPlayer(), Times.Once); mockContext.Verify(c => c.SaveChangesAsync(default), Times.Once); }
方案三:使用EF Core内存数据库(集成测试场景)
如果希望测试更贴近生产环境的真实运行逻辑,可以用EF Core的内存数据库替代Mock,直接操作真实实体对象:
[Fact] public async Task Handle_ShouldConfirmPlayer_InMemoryDb() { // 配置内存数据库 var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: "Test_ConfirmPlayer") .Options; using var context = new YourDbContext(options); var testPlayer = new Player { Id = Guid.NewGuid(), IsConfirmed = false }; context.Players.Add(testPlayer); await context.SaveChangesAsync(); var handler = new ConfirmPlayerCommandHandler(context); var request = new ConfirmPlayerCommand { PlayerId = testPlayer.Id }; // 执行目标方法 await handler.Handle(request, CancellationToken.None); // 验证数据库中的状态变更 var updatedPlayer = await context.Players.FirstAsync(p => p.Id == testPlayer.Id); Assert.True(updatedPlayer.IsConfirmed); }
内容的提问来源于stack exchange,提问作者Sabouei Alireza
相关产品推荐
相关产品推荐

