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

如何Mock依赖Cookie与Session的控制器Action并解决空引用异常?

问题分析与解决方案

你的测试代码触发空引用异常的核心原因是:先给控制器配置了带有效HttpContext的ControllerContext,紧接着又用空的Mock<ControllerContext>.Object覆盖了它,导致控制器的Request属性为null。此外,你还需要正确配置Cookie和Session才能让测试完整运行。


1. 修正ControllerContext赋值错误

删掉覆盖ControllerContext的无效代码,保留带DefaultHttpContext的正确配置:

// 移除这行错误代码:
// azureReportControllerUnderTest.ControllerContext = context.Object;

// 保留正确的上下文配置
azureReportControllerUnderTest.ControllerContext = new ControllerContext
{
    HttpContext = new DefaultHttpContext(),
};

2. 配置Request的Cookie值

GetCurrentTenant会读取Request.Cookies["selectedtenant"],需要在DefaultHttpContext中添加对应Cookie:

var httpContext = new DefaultHttpContext();
// 给Request添加目标Cookie
httpContext.Request.Cookies["selectedtenant"] = "3c6b60a7-8d30-4da3-bfdc-00691e899b5f";

azureReportControllerUnderTest.ControllerContext = new ControllerContext
{
    HttpContext = httpContext,
};

如果要测试无Cookie场景(即触发GetUserDefaultTenant分支),可以创建控制器的测试子类重写该protected方法:

// 测试用子类,重写GetUserDefaultTenant返回固定Guid
public class TestableAzureReportsController : AzureReportsController
{
    public TestableAzureReportsController(ILogger<AzureReportsController> logger, IPrismRepository prismRepo, IReportsRepository reportsRepo) 
        : base(logger, prismRepo, reportsRepo)
    {
    }

    protected override Guid GetUserDefaultTenant()
    {
        return Guid.Parse("3c6b60a7-8d30-4da3-bfdc-00691e899b5f");
    }
}

// 测试时使用这个子类替代原控制器
var azureReportControllerUnderTest = new TestableAzureReportsController(mockIlogger.Object, mockPrismRepository.Object, mockReportRepository.Object);

3. 配置Session支持

DefaultHttpContext默认未启用Session,需要手动配置Session服务及上下文:

// 注册Session所需服务
var services = new ServiceCollection();
services.AddSession();
var serviceProvider = services.BuildServiceProvider();

var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProvider;
// 初始化Session
await httpContext.Session.LoadAsync();

修改后的完整测试代码

[TestMethod]
public async Task GetCustomDateResourcesReturnsExcelWorkBook()
{
    var mockPrismRepository = new Mock<IPrismRepository>();
    var mockIlogger = new Mock<ILogger<AzureReportsController>>();
    var mockReportRepository = new Mock<IReportsRepository>();

    DateTime from = DateTime.Today;
    DateTime to = from.AddDays(30);

    var tenantId = Guid.Parse("3c6b60a7-8d30-4da3-bfdc-00691e899b5f");
    var excelWorkBook = new XLWorkbook();

    // 注意参数顺序:与Action中的调用保持一致(selectedTenant, startingDate, endingDate)
    mockReportRepository.Setup(r => r.GetCustomDateResources(tenantId, from, to))
                        .Returns(excelWorkBook);

    // 配置Session服务
    var services = new ServiceCollection();
    services.AddSession();
    var serviceProvider = services.BuildServiceProvider();

    var httpContext = new DefaultHttpContext();
    httpContext.RequestServices = serviceProvider;
    await httpContext.Session.LoadAsync();
    // 添加目标Cookie
    httpContext.Request.Cookies["selectedtenant"] = tenantId.ToString();

    var azureReportControllerUnderTest = new AzureReportsController(mockIlogger.Object, 
                                                                    mockPrismRepository.Object, 
                                                                    mockReportRepository.Object);
    azureReportControllerUnderTest.ControllerContext = new ControllerContext
    {
        HttpContext = httpContext,
    };

    var result = azureReportControllerUnderTest.ResourcesCustomReports(from, to);
    Assert.IsNotNull(result);

    excelWorkBook.Dispose();
    azureReportControllerUnderTest.Dispose();
}

额外注意事项

  • 检查GetCustomDateResources的参数顺序:你的原测试代码中Setup的参数顺序与Action调用不匹配,已在修正代码中调整。
  • 若GetUserDefaultTenant依赖其他服务,需同步Mock或配置这些服务的测试实例。

内容的提问来源于stack exchange,提问作者Tim Whatley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 03:35:33