使用[授权]集成测试Web Api

时间:2013-08-04 22:18:42

标签: c# asp.net-web-api web-config integration-testing authorize-attribute

所以我发现在[授权]标签上有一些点点滴滴,但没有解决我的问题。

我的场景是我有Web Api方法,我希望使用RestSharp进行集成测试。但是,RestSharp正在获取我的登录页面,而不是调用的结果。

[Authorize]
public Item GetItem([FromBody] int id) 
{
   return service.GetItem(id);
}

该产品使用自定义登录系统,我真正想要的是仅为集成测试禁用[授权]徽章的方法。但是我读到你可以允许匿名用户,它会“禁用”徽章,所以在解决方案中,我有一个集成测试项目,在那个项目中我有一个App.config文件。在那个文件中,我把:

 <location>
  <system.web>
   <authorization>
     <allow users="?"/>
    </authorization>
  </system.web>
 </location>

但这似乎也没有用。任何有关正在发生的事情的解释,为什么它不起作用以及如何使这项工作都将受到高度赞赏。

我试图设置一个Thread.CurrentPrincipal但是没有用(也许我做错了 - 你能在代码中设置“任何”授权吗?)。如果有帮助,则在httpmodule中处理身份验证。

3 个答案:

答案 0 :(得分:8)

我意识到这个问题是关于解雇&#39;真实的&#39; Restapharp在webapi端点上的请求,因此该建议不会立即适用于OP场景。但是:

我使用HttpConfigurationHttpServerHttpMessageInvoker使用内存中的Web Api测试(很像Badri's suggestion我相信)。通过这种方式,我不需要监听器或端口打开,因为我可以在内存中测试完整堆栈(端到端测试) - 在构建服务器,Heroku实例等上非常方便。

使用内存中测试,以下是设置Thread.CurrentPrincipal的方法..我在我的测试基类上有一个帮助,如下所示:

protected void AuthentateRequest()
{
    Thread.CurrentPrincipal = new AuthenticatedPrincipal(Thread.CurrentPrincipal);
}

使用它:

public class AuthenticatedPrincipal : IPrincipal
{
    private readonly IPrincipal _principalToWrap;
    private readonly IIdentity _identityToWrap;

    public AuthenticatedPrincipal(IPrincipal principalToWrap)
    {
        _principalToWrap = principalToWrap;
        _identityToWrap = new AuthenticatedIdentity(principalToWrap.Identity);
    }

    public bool IsInRole(string role)
    { return _principalToWrap.IsInRole(role); }

    public IIdentity Identity
    {
        get { return _identityToWrap; }
        private set { throw new NotSupportedException(); }
    }
}

public class AuthenticatedIdentity : IIdentity
{
    private readonly IIdentity _identityToWrap;

    public AuthenticatedIdentity(IIdentity identityToWrap)
    {
        _identityToWrap = identityToWrap;
    }

    public string Name
    {
        get { return _identityToWrap.Name; }
        private set { throw new NotSupportedException(); }
    }
    public string AuthenticationType
    {
        get { return _identityToWrap.AuthenticationType; }
        private set { throw new NotSupportedException(); }
    }
    public bool IsAuthenticated
    {
        get { return true; }
        private set { throw new NotSupportedException(); }
    }
}

手动存根IPrincipal可能看起来有点过分但我尝试使用我的mocking framework并且它在我的一些测试跑步者(Resharper和TeamCity,但不是NCrunch)中爆炸 - 有关序列化的事情我认为是AppDomains。

这会在Thread.CurrentPrincipal操作方法中设置ApiController,因此会误导AuthorizeAttribute相信您已通过身份验证。

答案 1 :(得分:5)

以下是设置Thread.CurrentPrincipal的方法。将这样的消息处理程序添加到Web API项目中,并在Register WebApiConfig.cs方法中添加处理程序,如下所示:config.MessageHandlers.Add(new MyTestHandler());

public class MyTestHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
                                 HttpRequestMessage request,
                                     CancellationToken cancellationToken)
    {
        var local = request.Properties["MS_IsLocal"] as Lazy<bool>;
        bool isLocal = local != null && local.Value;

        if (isLocal)
        {
            if (request.Headers.GetValues("X-Testing").First().Equals("true"))
            {
                var dummyPrincipal = new GenericPrincipal(
                                        new GenericIdentity("dummy", "dummy"),
                                          new[] { "myrole1" });

                Thread.CurrentPrincipal = dummyPrincipal;

                if (HttpContext.Current != null)
                    HttpContext.Current.User = dummyPrincipal;
            }
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

此处理程序设置经过身份验证的主体,以使您的所有[Authorize]满意。这种方法存在风险因素。仅用于测试,您应该将此处理程序插入Web API管道。如果将此处理程序插入生产代码中的管道(有意或无意),它基本上会破坏您的身份验证机制。为了在某​​种程度上降低风险(希望API不在本地访问),我检查以确保访问是本地的,并且标题X-Testing的值为true

从RestSharp中,添加自定义标题。

var request = new RestRequest(...);
request.AddHeader("X-Testing", "true");
BTW,对于集成测试,我宁愿使用内存托管,而不是网络托管。这样,Web API就可以在同一个测试项目中运行,您可以随心所欲地使用它,而不必担心会破坏生产中的某些内容。有关内存中托管的详细信息,请参阅thisthis

答案 2 :(得分:-1)

RestClient设置身份验证器:

RestClient.Authenticator = new HttpBasicAuthenticator(username, password);

使用自定义登录系统实际接受的身份验证器... Basic,NTLM,OAuth,Simple ...

http://restsharp.org/

的示例的第二行中记录了这种情况