You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:07:10