使用XUnit编写测试时遇Article空引用异常的解决方法
问题原因及解决办法
空引用异常的核心原因有三个:
- Mediator实例未注入控制器:测试中创建了
mediatorMock,但没有将其赋值给控制器的Mediator属性,导致控制器调用Mediator.Send()时实际是调用null对象的方法,返回null。 - Mediator的Setup匹配逻辑失效:使用具体的
createArticleCommand对象作为匹配条件,由于引用类型的相等性判断,实际执行时的命令对象和Setup中的对象引用不同,导致Setup不触发,返回null。 - UserService的Setup匹配逻辑不匹配控制器实际调用:控制器中调用
_userService.GetAsync(article.CreatedBy.ToString(), ...),但测试中Setup的是_currentUserMock.Object.GetUserId().ToString(),两者参数不一致,后续也会引发异常。
具体修复步骤
1. 确保控制器的依赖被正确注入
如果控制器通过构造函数接收依赖,测试时需将mock实例传入构造函数:
// 控制器构造函数示例 public class ArticlesController : ControllerBase { private readonly IMediator _mediator; private readonly IUserService _userService; public ArticlesController(IMediator mediator, IUserService userService) { _mediator = mediator; _userService = userService; } // ... 其他代码 }
测试中初始化控制器时传入mock:
// 替换原有控制器初始化代码 var mediatorMock = new Mock<IMediator>(); var userServiceMock = new Mock<IUserService>(); var _articlesController = new ArticlesController(mediatorMock.Object, userServiceMock.Object);
若控制器依赖是public属性,直接赋值即可:
_articlesController.Mediator = mediatorMock.Object; _articlesController._userService = userServiceMock.Object;
2. 修改Mediator的Setup逻辑
使用It.IsAny<>匹配任意参数,避免引用不匹配问题:
mediatorMock .Setup(m => m.Send(It.IsAny<CreateArticleCommand>(), It.IsAny<CancellationToken>())) .ReturnsAsync(new ArticleDto { Id = Guid.NewGuid(), CreatedBy = Guid.NewGuid() // 必须设置该值,否则后续ToString()会触发空引用 });
3. 修改UserService的Setup逻辑
匹配控制器实际调用的参数,可选择精确匹配或任意字符串匹配:
var mockArticle = new ArticleDto { Id = Guid.NewGuid(), CreatedBy = Guid.NewGuid() }; var user = new UserDto { Id = Guid.NewGuid(), Email = "test@test.com", FirstName = "Test", LastName = "User" }; // 先设置Mediator返回预设的mockArticle mediatorMock .Setup(m => m.Send(It.IsAny<CreateArticleCommand>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockArticle); // 精确匹配CreatedBy的字符串值 userServiceMock .Setup(x => x.GetAsync(mockArticle.CreatedBy.ToString(), It.IsAny<CancellationToken>())) .ReturnsAsync(user);
完整修复后的测试代码
[Fact] public async Task CreateAsync_ReturnsValidArticleDto() { // Arrange var cancellationToken = CancellationToken.None; var controllerContext = new ControllerContext { HttpContext = _httpContext.Object }; // 初始化mock与控制器 var mediatorMock = new Mock<IMediator>(); var userServiceMock = new Mock<IUserService>(); var _articlesController = new ArticlesController(mediatorMock.Object, userServiceMock.Object); _articlesController.ControllerContext = controllerContext; var contentTag = new ContentTagDto(1, "test") { Id = 1, Title = "test" }; var contentTagDtoList = new List<ContentTagDto>() { contentTag }; var createArticleCommand = new CreateArticleCommand("Title", "content", Guid.NewGuid(), ContentItemState.Draft, contentTagDtoList); // 准备预设的mock数据 var mockArticle = new ArticleDto { Id = Guid.NewGuid(), CreatedBy = Guid.NewGuid() }; var user = new UserDto { Id = Guid.NewGuid(), Email = "test@test.com", FirstName = "Test", LastName = "User" }; // 配置Mediator返回预设对象 mediatorMock .Setup(m => m.Send(It.IsAny<CreateArticleCommand>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockArticle); // 配置UserService返回预设用户 userServiceMock .Setup(x => x.GetAsync(mockArticle.CreatedBy.ToString(), It.IsAny<CancellationToken>())) .ReturnsAsync(user); // Act var result = await _articlesController.CreateAsync(cancellationToken, createArticleCommand); // Assert Assert.NotNull(result); Assert.Equal(user, result.CreatedByUser); Assert.Equal(user, result.LastModifiedByUser); }
内容的提问来源于stack exchange,提问作者Wahéb
相关产品推荐
相关产品推荐

