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

为依赖IActionDescriptorCollectionProvider的TagHelper单元测试配置依赖

解决方案:用NSubstitute模拟IActionDescriptorCollectionProvider

直接说结论:别费劲去实例化DefaultActionDescriptorCollectionProvider——它是ASP.NET Core的内部实现类,依赖一堆复杂的服务(比如IActionDescriptorProvider、IActionDescriptorChangeProvider),单元测试里不仅初始化成本极高,还没法直接修改它的ActionDescriptors.Items。最简便也最专业的方式就是用NSubstitute这类模拟框架来替代它。

具体实现步骤

  1. 构建自定义的ActionDescriptor集合
    手动创建ControllerActionDescriptor实例,设置好和测试场景匹配的Area、Controller、Action等路由属性,再把它们包装成ActionDescriptorCollection。

  2. 用NSubstitute生成模拟的依赖对象
    让这个模拟的IActionDescriptorCollectionProvider返回你刚才构建的自定义集合,这样TagHelper就能拿到你预设的路由信息。

  3. 注入模拟实例到TagHelper
    把模拟的提供者和HttpContextAccessor一起传入TagHelper构造函数,就能正常开展测试了。

修改后的Setup代码示例

using NSubstitute;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Http;

[SetUp] 
public void Setup() 
{ 
    // 初始化HttpContextAccessor
    var contextAccessor = new HttpContextAccessor(); 
    contextAccessor.HttpContext = new DefaultHttpContext(); 
    contextAccessor.HttpContext.Request.Path = "/foo"; 

    // 构建模拟的ActionDescriptor集合
    var testActionDescriptors = new List<ActionDescriptor>
    {
        new ControllerActionDescriptor
        {
            RouteValues = new Dictionary<string, string>
            {
                ["area"] = "",
                ["controller"] = "Home",
                ["action"] = "Index"
            }
            // 可根据测试需求添加更多属性,比如AttributeRouteInfo
        }
    };
    var actionDescriptorCollection = new ActionDescriptorCollection(testActionDescriptors, version: 1);

    // 用NSubstitute模拟IActionDescriptorCollectionProvider
    var actionDescriptorProvider = Substitute.For<IActionDescriptorCollectionProvider>();
    actionDescriptorProvider.ActionDescriptors.Returns(actionDescriptorCollection);

    // 初始化TagHelper
    var helper = new ActivePathSegmentTagHelper(contextAccessor, actionDescriptorProvider); 
    helper.Area = ""; 
    helper.Controller = "Home"; 
    helper.Action = "Index"; 
}

如何动态修改ActionDescriptors.Items?

ActionDescriptorCollection的Items是只读的,但你可以在测试方法里重新构建新集合,然后让模拟对象返回这个新集合:

[Test]
public void TestDifferentRouteScenario()
{
    // 构建新的ActionDescriptor集合
    var newDescriptors = new List<ActionDescriptor>
    {
        new ControllerActionDescriptor
        {
            RouteValues = new Dictionary<string, string>
            {
                ["area"] = "Admin",
                ["controller"] = "Dashboard",
                ["action"] = "Overview"
            }
        }
    };
    var newCollection = new ActionDescriptorCollection(newDescriptors, version: 2);

    // 让模拟提供者返回新集合
    actionDescriptorProvider.ActionDescriptors.Returns(newCollection);

    // 执行TagHelper逻辑并验证结果
    // ...
}

替代方案:手动实现测试用Provider

如果不想用模拟框架,也可以自己写一个极简的实现类:

public class TestActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider
{
    public ActionDescriptorCollection ActionDescriptors { get; set; }

    public TestActionDescriptorCollectionProvider(ActionDescriptorCollection collection)
    {
        ActionDescriptors = collection;
    }
}

然后在Setup里替换成这个类的实例:

var actionDescriptorProvider = new TestActionDescriptorCollectionProvider(actionDescriptorCollection);

这种方式虽然可行,但灵活性不如模拟框架(比如没法验证调用次数、动态修改返回值),所以还是优先推荐NSubstitute。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:48:00