如何在XUnit中从Header取Token并Mock服务类测试控制器
XUnit测试API:Mock服务类与Token问题解决方案
一、先修复业务代码中的潜在错误
你的代码里有几处大小写不匹配和空引用风险点,这会直接导致测试失败:
AdditionService中给AdditionDto赋值时,httpCode应改为**HttpCode**(属性定义是大写H)- 控制器里判断
result.Httpcode需改为**result.HttpCode** - 解析Claim时,
FirstOrDefault()后直接ToString()会导致空引用,需先判断Claim是否存在
二、用Moq实现IAdditionService的Mock
测试时使用Moq框架Mock服务接口,步骤如下:
1. 安装依赖包
先安装必要的NuGet包:
xunitMoqMicrosoft.AspNetCore.Mvc.Core
2. 完整测试类示例
using Xunit; using Moq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Http; using System.Security.Claims; public class AdditionControllerTests { private readonly Mock<IAddtionService> _mockService; private readonly AdditionController _controller; public AdditionControllerTests() { // 初始化Mock服务 _mockService = new Mock<IAddtionService>(); _controller = new AdditionController(_mockService.Object); // 模拟HttpContext,设置请求Header与用户身份 var httpContext = new DefaultHttpContext(); // 添加Authorization Header httpContext.Request.Headers["Authorization"] = "Bearer dummy-token"; // 模拟已解析的用户Claim,避免测试中真的解析JWT var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "test-user"), new Claim(ClaimTypes.Role, "Admin") // 满足[Authorize(Roles = "Admin")]要求 }, "Bearer")); _controller.ControllerContext = new ControllerContext { HttpContext = httpContext, User = user }; } [Fact] public void AddTwoNumbers_ValidInputs_ReturnsOk() { // Arrange int input1 = 2, input2 = 3; string userName = "test-user"; var expectedDto = new AdditionDto { HttpCode = 200, UserName = userName, Total = 5 }; // 设置Mock服务的返回逻辑 _mockService.Setup(s => s.AddingTwoNumbers(input1, input2, userName)) .Returns(expectedDto); // Act var result = _controller.AddTwoNumbers(input1, input2); // Assert var okResult = Assert.IsType<OkObjectResult>(result); var returnedDto = Assert.IsType<AdditionDto>(okResult.Value); Assert.Equal(expectedDto.Total, returnedDto.Total); // 验证Mock方法是否被正确调用 _mockService.Verify(s => s.AddingTwoNumbers(input1, input2, userName), Times.Once); } [Fact] public void AddTwoNumbers_InvalidModelState_ReturnsBadRequest() { // Arrange:手动添加模型错误 _controller.ModelState.AddModelError("input1", "输入不能为空"); // Act var result = _controller.AddTwoNumbers(0, 0); // Assert Assert.IsType<BadRequestResult>(result); } }
三、优化控制器的Token解析逻辑(避免空引用)
把控制器中Token解析的代码改为以下形式,从根源避免空引用异常:
public IActionResult AddTwoNumbers(int input1, int input2) { if (!ModelState.IsValid) return BadRequest(); var authHeader = HttpContext.Request.Headers["Authorization"].ToString(); if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ")) return Unauthorized("无效的授权头"); var tokenParts = authHeader.Split(' '); if (tokenParts.Length < 2) return BadRequest("授权头格式错误"); var handler = new JwtSecurityTokenHandler(); if (!handler.CanReadToken(tokenParts[1])) return BadRequest("无效的Token"); var decodedToken = handler.ReadJwtToken(tokenParts[1]); var nameClaim = decodedToken.Claims.FirstOrDefault(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"); if (nameClaim == null) return BadRequest("Token中缺少用户名称Claim"); var result = _additionService.AddingTwoNumbers(input1, input2, nameClaim.Value); return result.HttpCode == 200 ? Ok(result) : StatusCode(500, result); }
四、处理[Authorize]特性的测试
测试时要满足[Authorize(Roles = "Admin")]的要求,只需在模拟用户身份时添加Admin角色即可(如测试类构造函数中所示),无需处理真实的JWT验证流程。
内容的提问来源于stack exchange,提问作者Voggu venu
相关产品推荐
相关产品推荐

