Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试ASP.NETMVC Authorize属性以验证重定向到登录页面_C#_Asp.net Mvc - Fatal编程技术网

C# 单元测试ASP.NETMVC Authorize属性以验证重定向到登录页面

C# 单元测试ASP.NETMVC Authorize属性以验证重定向到登录页面,c#,asp.net-mvc,C#,Asp.net Mvc,这很可能是一个只需要另一双眼睛的例子。我肯定错过了什么,但我不明白为什么这种东西不能被测试。我基本上是试图通过将控制器标记为[Authorize]属性来确保未经身份验证的用户无法访问视图,我试图使用以下代码对此进行测试: [Fact] public void ShouldRedirectToLoginForUnauthenticatedUsers() { var mockControllerContext = new Mock<ControllerContext>()

这很可能是一个只需要另一双眼睛的例子。我肯定错过了什么,但我不明白为什么这种东西不能被测试。我基本上是试图通过将控制器标记为[Authorize]属性来确保未经身份验证的用户无法访问视图,我试图使用以下代码对此进行测试:

[Fact]
public void ShouldRedirectToLoginForUnauthenticatedUsers()
{
    var mockControllerContext = new Mock<ControllerContext>()
                         { DefaultValue = DefaultValue.Mock };
    var controller = new MyAdminController() 
              {ControllerContext = mockControllerContext.Object};
    mockControllerContext.Setup(c =>
               c.HttpContext.Request.IsAuthenticated).Returns(false);
    var result = controller.Index();
    Assert.IsAssignableFrom<RedirectResult>(result);
}

感谢您的帮助…

您的测试级别不正确。[Authorize]属性确保路由引擎永远不会为未经授权的用户调用该方法-重定向结果实际上来自路由,而不是来自控制器方法

好消息是——已经有了这方面的测试覆盖(作为MVC框架源代码的一部分),所以我想说您不必担心它;只要确保您的控制器方法在被调用时做了正确的事情,并相信框架不会在错误的情况下调用它

编辑:如果要验证单元测试中是否存在该属性,则需要使用反射检查控制器方法,如下所示。此示例将验证随MVC2一起安装的“New ASP.NET MVC 2 Project”演示中ChangePassword POST方法上是否存在Authorize属性

[TestFixture]
public class AccountControllerTests {

    [Test]
    public void Verify_ChangePassword_Method_Is_Decorated_With_Authorize_Attribute() {
        var controller = new AccountController();
        var type = controller.GetType();
        var methodInfo = type.GetMethod("ChangePassword", new Type[] { typeof(ChangePasswordModel) });
        var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
        Assert.IsTrue(attributes.Any(), "No AuthorizeAttribute found on ChangePassword(ChangePasswordModel model) method");
    }
}

为什么不使用反射来查找控制器类和/或您正在测试的操作方法上的
[Authorize]
属性呢?假设框架确实确保该属性得到尊重,这将是最容易做到的事情

您可能在错误的级别进行测试,但这是一个有意义的测试。我的意思是,如果我用authorize(Roles=“Superhero”)属性标记一个方法,如果我标记它,我实际上不需要测试。我(认为我)想要的是测试未经授权的用户没有访问权限,而授权用户有访问权限

对于未经授权的用户,进行如下测试:

// Arrange
var user = SetupUser(isAuthenticated, roles);
var controller = SetupController(user);

// Act
SomeHelper.Invoke(controller => controller.MyAction());

// Assert
Assert.AreEqual(401,
  controller.ControllerContext.HttpContext.Response.StatusCode, "Status Code");
嗯,这不容易,花了我10个小时,但在这里。我希望有人能从中受益,或者说服我从事另一种职业。:)(顺便说一句,我正在使用rhino mock)

但是,如果没有此帮助器功能,这不是很有用:

public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated)
{
    var context = mocks.StrictMock<HttpContextBase>();
    var request = mocks.StrictMock<HttpRequestBase>();
    var response = mocks.StrictMock<HttpResponseBase>();
    var session = mocks.StrictMock<HttpSessionStateBase>();
    var server = mocks.StrictMock<HttpServerUtilityBase>();
    var cachePolicy = mocks.Stub<HttpCachePolicyBase>();
    var user = mocks.StrictMock<IPrincipal>();
    var identity = mocks.StrictMock<IIdentity>();
    var itemDictionary = new Dictionary<object, object>();

    identity.Expect(id => id.IsAuthenticated).Return(isAuthenticated);
    user.Expect(u => u.Identity).Return(identity).Repeat.Any();

    context.Expect(c => c.User).PropertyBehavior();
    context.User = user;
    context.Expect(ctx => ctx.Items).Return(itemDictionary).Repeat.Any();
    context.Expect(ctx => ctx.Request).Return(request).Repeat.Any();
    context.Expect(ctx => ctx.Response).Return(response).Repeat.Any();
    context.Expect(ctx => ctx.Session).Return(session).Repeat.Any();
    context.Expect(ctx => ctx.Server).Return(server).Repeat.Any();

    response.Expect(r => r.Cache).Return(cachePolicy).Repeat.Any();
    response.Expect(r => r.StatusCode).PropertyBehavior();

    return context;
}
公共静态HttpContextBase FakeHttpContext(MockRepository mocks,bool已验证)
{
var context=mocks.StrictMock();
var request=mocks.StrictMock();
var response=mocks.StrictMock();
var session=mocks.StrictMock();
var server=mocks.StrictMock();
var cachePolicy=mocks.Stub();
var user=mocks.StrictMock();
var identity=mocks.StrictMock();
var itemDictionary=新字典();
identity.Expect(id=>id.IsAuthenticated).Return(IsAuthenticated);
Expect(u=>u.Identity).Return(Identity.Repeat.Any();
Expect(c=>c.User).PropertyBehavior();
context.User=User;
Expect(ctx=>ctx.Items).Return(itemDictionary).Repeat.Any();
Expect(ctx=>ctx.Request).Return(Request).Repeat.Any();
Expect(ctx=>ctx.Response).Return(Response.Repeat.Any();
Expect(ctx=>ctx.Session).Return(Session).Repeat.Any();
Expect(ctx=>ctx.Server).Return(Server.Repeat.Any();
Expect(r=>r.Cache).Return(cachePolicy).Repeat.Any();
Expect(r=>r.StatusCode).PropertyBehavior();
返回上下文;
}
因此,您可以确认不在角色中的用户没有访问权限。我试着写一个测试来证实相反的情况,但在mvc管道中挖掘了两个多小时后,我将把它留给手动测试人员。(当我进入VirtualPath ProviderViewEngine类时,我退出了。WTF?我不想做任何事,比如说VirtualPath、Provider或ViewEngine——这三者的结合!)


我很好奇,在一个所谓的“可测试”框架中,这为什么如此困难

我不同意Dylan的回答,因为“用户必须登录”并不意味着“控制器方法用AuthorizeAttribute注释”

为了确保在调用action方法时“用户必须登录”,ASP.NET MVC框架会执行类似的操作(请稍候,它最终会变得更简单)

在工作的c代码中很难实现这个伪脚本。很可能,这使得设置这样的测试变得非常简单,并在幕后执行这些步骤。这里有一个例子

首先从nuget安装软件包(撰写本文时版本为1.4.0-beta4)

PM>安装包Xania.AspNet.Simulator-预安装

然后,您的测试方法可能如下所示(假设安装了NUnit和FluentAssertions):


对于.NET Framework,我们使用此类来验证每个MVC和API控制器是否具有
AuthorizeAttribute
,以及每个API控制器是否应该具有
RoutePrefixAttribute

[TestFixture]
public class TestControllerHasAuthorizeRole
{
    private static IEnumerable<Type> GetChildTypes<T>()
    {
        var types = typeof(Startup).Assembly.GetTypes();
        return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
    }

    [Test]
    public void MvcControllersShouldHaveAuthrorizeAttribute()
    {
        var controllers = GetChildTypes<Controller>();
        foreach (var controller in controllers)
        {
            var authorizeAttribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Mvc.AuthorizeAttribute), true) as System.Web.Mvc.AuthorizeAttribute;
            Assert.IsNotNull(authorizeAttribute, $"MVC-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveAuthorizeAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.AuthorizeAttribute), true) as System.Web.Http.AuthorizeAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveRoutePrefixAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.RoutePrefixAttribute), true) as System.Web.Http.RoutePrefixAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement RoutePrefixAttribute");
            Assert.IsTrue(attribute.Prefix.StartsWith("api/", StringComparison.OrdinalIgnoreCase), $"API-controller {controller.FullName} does not have a route prefix that starts with api/");
        }
    }
}

谢谢Dylan-我想我可能测试错了。我很高兴“假设”如果控制器被击中,用户就被验证。另外,你确定它已经在框架中测试过了吗?我可以看到一些测试提供有效的IPrincipal,但没有一个测试无效的情况;-)呃,不。。。我自己还没有真正验证过这个测试用例;我相信MVC团队会做对的。我的错!我喜欢这个答案,因为它不是正确的方法,但我不相信“该特性在框架中测试并工作”这一论点。我相信该属性工作正常,这是框架的工作,但我仍然希望声明我的控制器的哪些方法使用该属性。@Mathias-有关如何使用反射来验证所需属性的存在的示例,请参见编辑。
the[Authorize]属性确保路由引擎不会为未经授权的用户调用该方法-重定向结果实际上来自路由,而不是来自控制器方法。
-这句话的所有内容都是错误的。路由发生在身份验证之前很久
public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated)
{
    var context = mocks.StrictMock<HttpContextBase>();
    var request = mocks.StrictMock<HttpRequestBase>();
    var response = mocks.StrictMock<HttpResponseBase>();
    var session = mocks.StrictMock<HttpSessionStateBase>();
    var server = mocks.StrictMock<HttpServerUtilityBase>();
    var cachePolicy = mocks.Stub<HttpCachePolicyBase>();
    var user = mocks.StrictMock<IPrincipal>();
    var identity = mocks.StrictMock<IIdentity>();
    var itemDictionary = new Dictionary<object, object>();

    identity.Expect(id => id.IsAuthenticated).Return(isAuthenticated);
    user.Expect(u => u.Identity).Return(identity).Repeat.Any();

    context.Expect(c => c.User).PropertyBehavior();
    context.User = user;
    context.Expect(ctx => ctx.Items).Return(itemDictionary).Repeat.Any();
    context.Expect(ctx => ctx.Request).Return(request).Repeat.Any();
    context.Expect(ctx => ctx.Response).Return(response).Repeat.Any();
    context.Expect(ctx => ctx.Session).Return(session).Repeat.Any();
    context.Expect(ctx => ctx.Server).Return(server).Repeat.Any();

    response.Expect(r => r.Cache).Return(cachePolicy).Repeat.Any();
    response.Expect(r => r.StatusCode).PropertyBehavior();

    return context;
}
let $filters = All associated filter attributes which implement
               IAuthorizationFilter

let $invoker = instance of type ControllerActionInvoker
let $ctrlCtx = instance or mock of type ControllerContext
let $actionDesc = instance or mock of type ActionDescriptor
let $authzCtx = $invoker.InvokeAuthorizationFilters($ctrlCtx, $filters, $actionDesc);

then controller action is authorized when $authzCtx.Result is not null 
[Test]
public void AnonymousUserIsNotAuthorized()
{
  // arrange
  var action = new ProfileController().Action(c => c.Index());
  // act
  var result = action.GetAuthorizationResult();
  // assert
  result.Should().NotBeNull(); 
}

[Test]
public void LoggedInUserIsAuthorized()
{
  // arrange
  var action = new ProfileController().Action(c => c.Index())
     // simulate authenticated user
     .Authenticate("user1", new []{"role1"});
  // act
  var result = action.GetAuthorizationResult();
  // assert
  result.Should().BeNull(); 
}
[TestFixture]
public class TestControllerHasAuthorizeRole
{
    private static IEnumerable<Type> GetChildTypes<T>()
    {
        var types = typeof(Startup).Assembly.GetTypes();
        return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
    }

    [Test]
    public void MvcControllersShouldHaveAuthrorizeAttribute()
    {
        var controllers = GetChildTypes<Controller>();
        foreach (var controller in controllers)
        {
            var authorizeAttribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Mvc.AuthorizeAttribute), true) as System.Web.Mvc.AuthorizeAttribute;
            Assert.IsNotNull(authorizeAttribute, $"MVC-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveAuthorizeAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.AuthorizeAttribute), true) as System.Web.Http.AuthorizeAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveRoutePrefixAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.RoutePrefixAttribute), true) as System.Web.Http.RoutePrefixAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement RoutePrefixAttribute");
            Assert.IsTrue(attribute.Prefix.StartsWith("api/", StringComparison.OrdinalIgnoreCase), $"API-controller {controller.FullName} does not have a route prefix that starts with api/");
        }
    }
}
public class AuthorizeAttributeTest
{
    private static IEnumerable<Type> GetChildTypes<T>()
    {
        var types = typeof(Startup).Assembly.GetTypes();
        return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
    }

    [Fact]
    public void ApiAndMVCControllersShouldHaveAuthorizeAttribute()
    {
        var controllers = GetChildTypes<ControllerBase>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(Microsoft.AspNetCore.Authorization.AuthorizeAttribute), true) as Microsoft.AspNetCore.Authorization.AuthorizeAttribute;
            Assert.NotNull(attribute);
        }
    }
}