如何解决Xunit测试中‘Assert.IsAssignableFrom() Failure: Value is null’错误?
ASP.NET Core TDD测试Create接口时Assert.IsAssignableFrom()返回null的问题
测试时触发的错误提示:
Assert.IsAssignableFrom() Failure: Value is null.具体断言失败信息:
Expected: typeof(Microsoft.AspNetCore.Mvc.CreatedAtRouteResult) Actual: null
Repository代码
public class Repository { private IList<Subject> _subjects = new List<Subject>() { new Subject() { SubjectId = Guid.Parse("1fe63de5-4408-4d7b-a40a-bad231b7bd9f"), Title = "Math", Credits = 8 }, new Subject() { SubjectId = Guid.Parse("de331c36-6b31-4dd9-b50d-f677e51b987c"), Title = "Chemistry", Credits = 10, Teacher = new Teacher(){ Dni = 2210890, FirstName = "Albert", SurName = "Einstein", Email = "Albert_Einstein@gmail.com", }, Students = new List<Student>() { new Student() { FirstName = "Cristiano", SurName = "Ronaldo", Email = "CristianoRonaldo12@gmail.com", EnrollmentDate = DateTime.UtcNow }, new Student() { FirstName = "Leonel", SurName = "Messi", Email = "LeonelMessi12@gmail.com", EnrollmentDate = DateTime.UtcNow } } } }; }
控制器代码
[Route("api/[controler]/[action]")] [ApiController] public class SubjectController : ControllerBase { private Repository _repo; public SubjectController(Repository repo) { _repo = repo; } public IActionResult CreateSubject(Subject subject) { if(subject == null) { Console.WriteLine("Subject Object sent is null"); } _repo.Create(subject); return RedirectToAction(nameof(Index)); } }
Xunit测试代码
public class UnitTest2 { private SubjectController _context; private Repository _repo; public UnitTest2() { _repo = new Repository(); _context = new SubjectController(_repo); } [Fact] public void CreateSubjectTest() { //Arrange var subject = new Subject() { SubjectId = Guid.Parse("761c6dc1-4b02-4db5-bf91-5452efb44c1a"), Title = "History", Credits = 10, }; //Act var result = _context.CreateSubject(subject) as ObjectResult; //Assert Assert.IsAssignableFrom<CreatedAtRouteResult>(result); Assert.Equal((int)HttpStatusCode.Created, result!.StatusCode); } };
问题原因与修复方案
1. 核心问题:返回类型不匹配
测试代码期望控制器返回CreatedAtRouteResult(ObjectResult子类),但实际控制器返回的是RedirectToActionResult,用as ObjectResult转换后自然得到null,导致断言失败。同时控制器的Create接口不符合REST规范。
2. 分步修复
第一步:修正控制器路由拼写错误
原路由中的[controler]是拼写错误,改为[controller]:
[Route("api/[controller]/[action]")]
第二步:完善控制器CreateSubject方法
遵循REST规范,POST创建资源后返回201 Created状态码,同时返回创建的资源:
[HttpPost] // 添加HttpPost特性,明确请求类型 public IActionResult CreateSubject(Subject subject) { if(subject == null) { return BadRequest("Subject cannot be null"); // 返回400错误,替代控制台输出 } // 确保Repository的Create方法正确实现 _repo.Create(subject); // 返回201 Created,并指向获取该资源的接口 return CreatedAtAction(nameof(GetSubject), new { id = subject.SubjectId }, subject); } // 配套添加获取单个Subject的接口(用于CreatedAtAction路由跳转) [HttpGet("{id}")] public IActionResult GetSubject(Guid id) { var subject = _repo._subjects.FirstOrDefault(s => s.SubjectId == id); if(subject == null) { return NotFound(); } return Ok(subject); }
同时补充Repository的Create方法实现:
public void Create(Subject subject) { _subjects.Add(subject); }
第三步:修改测试代码匹配返回类型
[Fact] public void CreateSubjectTest() { //Arrange var subject = new Subject() { SubjectId = Guid.Parse("761c6dc1-4b02-4db5-bf91-5452efb44c1a"), Title = "History", Credits = 10, }; //Act var result = _context.CreateSubject(subject) as CreatedAtActionResult; //Assert Assert.NotNull(result); // 先断言结果不为null Assert.IsAssignableFrom<CreatedAtActionResult>(result); Assert.Equal((int)HttpStatusCode.Created, result.StatusCode); // 验证返回的资源正确性 var returnedSubject = result.Value as Subject; Assert.NotNull(returnedSubject); Assert.Equal("History", returnedSubject.Title); // 验证Repository中已添加新Subject Assert.Equal(3, _repo._subjects.Count); // 初始2个,添加后应为3个 }
内容的提问来源于stack exchange,提问作者Turing
相关产品推荐
相关产品推荐

