entre - Pruebas unitarias ASP.Net MVC Autorizar atributo para verificar el redireccionamiento a la página de inicio de sesión
web forms c# ejemplos (4)
Probablemente, este sea un caso de solo necesitar otro par de ojos. Debo extrañar algo, pero no puedo entender por qué este tipo de cosas no pueden ser probadas. Básicamente estoy tratando de asegurar que los usuarios no autenticados no puedan acceder a la vista marcando el controlador con el atributo [Autorizar] y estoy tratando de probar esto usando el siguiente código:
[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);
}
El RedirectResult que estoy buscando es una especie de indicación de que el usuario está siendo redirigido al formulario de inicio de sesión, pero en su lugar siempre se devuelve un ViewResult y cuando se depura puedo ver que el método Index () se ejecuta correctamente aunque el usuario esté no autenticado.
¿Estoy haciendo algo mal? Probando en el nivel equivocado? ¿Debería estar probando en el nivel de ruta para este tipo de cosas?
Sé que el atributo [Autorizar] funciona, porque cuando hago girar la página, la pantalla de inicio de sesión se me impone, pero ¿cómo puedo verificar esto en una prueba?
El controlador y el método de índice son muy simples solo para que pueda verificar el comportamiento. Los he incluido para completar:
[Authorize]
public class MyAdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
Cualquier ayuda apreciada ...
Estás probando en el nivel equivocado. El atributo [Autorizar] asegura que el motor de enrutamiento nunca invocará ese método para un usuario no autorizado: el RedirectResult en realidad procederá de la ruta, no de su método de controlador.
La buena noticia es que ya hay cobertura de prueba para esto (como parte del código fuente del marco MVC), así que diría que no necesita preocuparse por eso; solo asegúrese de que su método de control haga lo correcto cuando se llame, y confíe en que el marco no lo llame en las circunstancias incorrectas.
EDITAR: Si desea verificar la presencia del atributo en las pruebas de su unidad, necesitará usar la reflexión para inspeccionar los métodos de su controlador de la siguiente manera. Este ejemplo verificará la presencia del atributo Authorize en el método ChangePassword POST en la demostración ''New ASP.NET MVC 2 Project'' que está instalada con MVC2.
[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");
}
}
¿Por qué no usar el reflejo para buscar el atributo [Authorize]
en la clase de controlador y / o el método de acción que está probando? Suponiendo que el marco se asegure de que se respete el atributo, esto sería lo más fácil de hacer.
Bueno, es posible que esté probando en el nivel equivocado, pero es la prueba que tiene sentido. Quiero decir, si marcó un método con el atributo authorize (Roles = "Superhero"), realmente no necesito una prueba si lo señalé. Lo que yo (creo que) quiero es probar que un usuario no autorizado no tiene acceso y que lo hace un usuario autorizado.
Para un usuario no autorizado, una prueba como esta:
// 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");
Bueno, no es fácil y me tomó 10 horas, pero aquí está. Espero que alguien se pueda beneficiar de eso o me convenza de ir a otra profesión. :) (Por cierto, estoy usando el simulacro de rinoceronte)
[Test]
public void AuthenticatedNotIsUserRole_Should_RedirectToLogin()
{
// Arrange
var mocks = new MockRepository();
var controller = new FriendsController();
var httpContext = FakeHttpContext(mocks, true);
controller.ControllerContext = new ControllerContext
{
Controller = controller,
RequestContext = new RequestContext(httpContext, new RouteData())
};
httpContext.User.Expect(u => u.IsInRole("User")).Return(false);
mocks.ReplayAll();
// Act
var result =
controller.ActionInvoker.InvokeAction(controller.ControllerContext, "Index");
var statusCode = httpContext.Response.StatusCode;
// Assert
Assert.IsTrue(result, "Invoker Result");
Assert.AreEqual(401, statusCode, "Status Code");
mocks.VerifyAll();
}
Aunque, eso no es muy útil sin esta función auxiliar:
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;
}
Entonces, eso le confirma a usted que los usuarios que no tienen un rol no tienen acceso. Traté de escribir una prueba para confirmar lo contrario, pero después de dos horas más de excavar a través de mvc fontanería, lo dejo a los probadores manuales. (Salí cuando llegué a la clase VirtualPathProviderViewEngine. WTF? No quiero que nada haga un VirtualPath o un Provider o ViewEngine ¡mucho la unión de los tres!)
Tengo curiosidad de por qué esto es tan difícil en un marco supuestamente "comprobable".
No estoy de acuerdo con la respuesta de Dylan, porque ''el usuario debe estar conectado'' no implica que ''el método del controlador esté anotado con AuthorizeAttribute''
para asegurar que ''el usuario debe estar conectado'' cuando llame al método de acción, el marco MVC de ASP.NET hace algo como esto (solo agárrese, con el tiempo será más simple)
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
Es difícil implementar este pseudo script en un código de trabajo c #. Probablemente, Xania.AspNet.Simulator simplifica la configuración de una prueba como esta y realiza exactamente estos pasos debajo de la cubierta. Aquí hay un ejemplo.
primero instale el paquete de Nuget (versión 1.4.0-beta4 en el momento de la escritura)
PM> install-package Xania.AspNet.Simulator -Pre
Entonces su método de prueba podría verse así (suponiendo que NUnit y FluentAssertions están instaladas):
[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();
}