单元测试控制器报错:使用MS Testing测试控制器时出现异常
问题排查与修复方案
先梳理测试代码及相关逻辑中的核心问题,再给出针对性修复方案:
1. 控制器实例化语法错误
测试代码里的控制器初始化逻辑完全错误:
_Controller = ProductCatsController(_Controller.Object);
正确做法是传入已Setup好的_categoryRepository.Object,以及需要Mock的IMapper实例(控制器构造函数要求两个参数),你需要先创建Mock<IMapper>实例,再传入控制器。
2. 未Setup AutoMapper映射逻辑
控制器CreateCategory方法两次调用_mapper.Map:
- 将
CreateCatDto映射为ProductCat - 将
ProductCat映射为CategoryDto
若测试未对这两个映射做Setup,Mapper会返回null,后续调用CatForReturn.Id时会触发NullReferenceException。
3. 测试方法标记async但无await操作
测试方法声明为public async Task PostDataReturnOk(),但体内没有任何await调用,会导致测试被当作同步执行,引发不可预期行为。如果AddCat是同步方法,直接移除async,改为public void PostDataReturnOk();如果是异步方法,需确保用await并返回Task。
4. 未调用待测试的CreateCategory方法
当前测试仅Setup了Repository、实例化了控制器,但完全没有调用目标测试方法,未达到测试目的。
5. 未处理[Authorize]属性
控制器方法带有[Authorize(Roles = "Admin")],默认测试时会返回401 Unauthorized,需在测试中模拟用户授权,或绕过授权验证。
修正后的完整测试代码示例
假设使用Moq框架,修正后的测试代码如下:
// 类级别声明Mock实例 private Mock<IProCatRepository> _categoryRepository; private Mock<IMapper> _mapper; private ProductCatsController _controller; private Fixture _fixture; [TestInitialize] // MS Test初始化方法 public void Setup() { _fixture = new Fixture(); _categoryRepository = new Mock<IProCatRepository>(); _mapper = new Mock<IMapper>(); // 初始化控制器 _controller = new ProductCatsController(_categoryRepository.Object, _mapper.Object); // 模拟Admin角色授权 var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Role, "Admin") })); _controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = user } }; } [TestMethod] public void PostDataReturnCreatedResult() { // 1. 准备测试数据 var createCatDto = _fixture.Create<CreateCatDto>(); var productCat = _fixture.Create<ProductCat>(); var categoryDto = _fixture.Create<CategoryDto>(); // 2. Setup Mapper映射 _mapper.Setup(m => m.Map<ProductCat>(createCatDto)).Returns(productCat); _mapper.Setup(m => m.Map<CategoryDto>(productCat)).Returns(categoryDto); // 3. Setup Repository的AddCat方法 _categoryRepository.Setup(re => re.AddCat(It.IsAny<ProductCat>())).Returns(productCat); // 4. 调用待测试方法 var result = _controller.CreateCategory(createCatDto); // 5. 断言结果符合预期 var createdAtRouteResult = result.Result as CreatedAtRouteResult; Assert.IsNotNull(createdAtRouteResult); Assert.AreEqual("GetCat", createdAtRouteResult.RouteName); Assert.AreEqual(categoryDto.Id, (createdAtRouteResult.RouteValues["id"] as int?)?.Value); Assert.AreEqual(categoryDto, createdAtRouteResult.Value); // 验证依赖方法是否被正确调用 _categoryRepository.Verify(re => re.AddCat(productCat), Times.Once); _mapper.Verify(m => m.Map<ProductCat>(createCatDto), Times.Once); _mapper.Verify(m => m.Map<CategoryDto>(productCat), Times.Once); }
额外说明
- 若
AddCat是异步方法(返回Task<ProductCat>),需将测试方法改为async Task,调用时用await,同时Setup时用ReturnsAsync:_categoryRepository.Setup(re => re.AddCat(It.IsAny<ProductCat>())).ReturnsAsync(productCat); var result = await _controller.CreateCategory(createCatDto); - 确保已安装必要NuGet包:
Moq、AutoMapper、AutoFixture(若使用)、Microsoft.AspNetCore.Mvc.Testing(若需完整测试上下文)。
内容的提问来源于stack exchange,提问作者Malshan Hansaka
相关产品推荐
相关产品推荐

