【问题标题】:Unit testing attribute routing in WebApi2WebApi2中的单元测试属性路由
【发布时间】:2015-08-31 17:37:01
【问题描述】:

我正在使用 WebApi2 attrbiute 路由项目,我正在尝试对路由进行单元测试(请求正在控制器中执行正确的 api 方法)。但到目前为止我无法让它工作......

似乎它没有选择属性中定义的路线 知道可能有什么问题或遗漏吗?

提前致谢!吉列尔莫。

这是我的单元测试代码:

var config = new HttpConfiguration
{
    IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always,
};

config.Routes.MapHttpRoute(name: "Default", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });

request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/homes/report");
route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
routeData = new HttpRouteData(route, new HttpRouteValueDictionary
    {
        { "controller", "homes" },
    });

config.MapHttpAttributeRoutes();
config.EnsureInitialized();

controller = new HomesController
{
    Catalog = catalog.Object,
    ControllerContext = new HttpControllerContext(config, routeData, request),
    Request = request,
};

controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;

var routeTester = new RouteTester(config, request, controller.ControllerContext);

Assert.IsTrue(routeTester.CompareSignatures(ReflectionHelpers.GetMethodInfo((HomesController p) => p.GetHomesReport())));

RouteTester.cs

public class RouteTester
{
    readonly HttpConfiguration config;
    readonly HttpRequestMessage request;
    readonly IHttpRouteData routeData;
    readonly IHttpControllerSelector controllerSelector;
    readonly HttpControllerContext controllerContext;

    public RouteTester(HttpConfiguration conf, HttpRequestMessage req, HttpControllerContext context)
    {
        config = conf;
        request = req;
        controllerContext = context;
        routeData = context.RouteData;

        controllerSelector = new DefaultHttpControllerSelector(config);
    }

    public string GetActionName()
    {
        if (controllerContext.ControllerDescriptor == null)
            GetControllerType();

        var actionSelector = new ApiControllerActionSelector();
        var descriptor = actionSelector.SelectAction(controllerContext);

        return descriptor.ActionName;
    }

    public Type GetControllerType()
    {
        var descriptor = controllerSelector.SelectController(request);
        controllerContext.ControllerDescriptor = descriptor;
        return descriptor.ControllerType;
    }

    public bool CompareSignatures(MethodInfo method)
    {
        if (controllerContext.ControllerDescriptor == null)
            GetControllerType();


        var actionSelector = new ApiControllerActionSelector();
        var x = actionSelector.GetActionMapping(controllerContext.ControllerDescriptor)[request.Method.ToString()];


        return x.Any(item => ((MethodBase)(((ReflectedHttpActionDescriptor)item).MethodInfo)).ToString() == ((MethodBase)method).ToString());
    }

ReflectionHelpers.cs

 public class ReflectionHelpers
{
    public static string GetMethodName<T, U>(Expression<Func<T, U>> expression)
    {
        var method = expression.Body as MethodCallExpression;

        if (method != null)
            return method.Method.Name;

        throw new ArgumentException("Expression is wrong");
    }

    public static MethodInfo GetMethodInfo<T, U>(Expression<Func<T, U>> expression)
    {
        var method = expression.Body as MethodCallExpression;
        if (method != null)
            return method.Method;


        throw new ArgumentException("Expression is wrong");
    }


    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var method = expression.Body as MethodCallExpression;
        if (method != null)
            return method.Method;


        throw new ArgumentException("Expression is wrong");
    }
}

控制器 sn-p

[Route("api/homes/homereport")]
public void GetHomesReport()
{
    var homeReportItems = HomeReport();
}

【问题讨论】:

    标签: asp.net-web-api2 asp.net-web-api asp.net-web-api-routing


    【解决方案1】:

    不确定这是否正是您问题的解决方案,但我使用这种方法来控制属性路由。 它强制你指定控制器类和方法签名,但都没有字符串文字,所以你在这里得到很好的编译时间验证。 FunctionSignature 作为公共字符串Action(int value) 的简单方法,它看起来像这样Func&lt;int, string&gt;

    测试方法如下所示:

    public void TestMethod() {
        var info = GetEndpointInfo<ControllerClass, FunctionSignature>(c => c.Action);
    
        Assert.IsNotNull(info.AllowsAnonymous);
        Assert.AreEqual("data/places/{query}", info.Route);
    }
    

    这里有一些 lambda 和反射暴力来获取元信息:

    private EndpointInfo GetEndpointInfo<TController, TMethod>(Expression<Func<TController, TMethod>> expression) {
        var controllerType = typeof(TController);
        var prefix = controllerType.GetCustomAttribute<RoutePrefixAttribute>().Prefix;
        MethodInfo methodInfo = GetMemberInfo(expression);
        var template = methodInfo.GetCustomAttribute<RouteAttribute>().Template;
    
        var info = new EndpointInfo() {
            AllowsAnonymous = controllerType.GetCustomAttribute<AllowAnonymousAttribute>() != null, //etend here to check method level attr
            Route = prefix + "/" + template
        };
        return info;
    }
    
    private MethodInfo GetMemberInfo<TController, TMethod>(Expression<Func<TController, TMethod>> expression) {
        var unaryExpression = expression.Body as UnaryExpression;
        var methodCall = unaryExpression.Operand as MethodCallExpression;
        var constant = methodCall.Object as ConstantExpression;
        var methodInfo = constant.Value as MethodInfo;
        return methodInfo;
    }
    class EndpointInfo{
        public bool AllowsAnonymous { get; set; }
        public string Route { get; set; }
    }
    

    希望这会有所帮助。

    【讨论】:

    • @polonskyg 创建它,我省略了它以节省空间。
    • 但是它应该有什么特别的吗?可以上传吗?
    • 不,没什么特别的,反正已经更新了答案
    猜你喜欢
    • 2014-01-12
    • 2015-10-05
    • 2014-09-17
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 2020-08-15
    • 1970-01-01
    相关资源
    最近更新 更多