Unité testant l’atsortingbut ASP.Net MVC Authorize pour vérifier la redirection vers la page de connexion

Cela va probablement être un cas de besoin d’une autre paire d’yeux. Je dois manquer quelque chose, mais je ne peux pas comprendre pourquoi ce genre de chose ne peut pas être testé. J’essaie essentiellement de faire en sorte que les utilisateurs non authentifiés ne puissent pas accéder à la vue en marquant le contrôleur avec l’atsortingbut [Authorize] et en essayant de le tester en utilisant le code suivant:

[Fact] public void ShouldRedirectToLoginForUnauthenticatedUsers() { var mockControllerContext = new Mock() { 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(result); } 

Le RedirectResult que je recherche est une sorte d’indication que l’utilisateur est redirigé vers le formulaire de connexion, mais à la place, ViewResult est toujours renvoyé et lors du débogage, je peux voir que la méthode Index () est bien réussie même si l’utilisateur est Non authentifié.

Est-ce que je fais quelque chose de mal? Tester au mauvais niveau? Devrais-je plutôt tester au niveau de la route pour ce genre de chose?

Je sais que l’atsortingbut [Authorize] fonctionne, car lorsque je lance la page, l’écran de connexion m’est forcé – mais comment puis-je le vérifier dans un test?

Le contrôleur et la méthode d’indexation sont très simples pour que je puisse vérifier le comportement. Je les ai inclus pour être complet:

 [Authorize] public class MyAdminController : Controller { public ActionResult Index() { return View(); } } 

Toute aide appréciée …

Vous testez au mauvais niveau. L’atsortingbut [Authorize] garantit que le moteur de routage n’invoquera jamais cette méthode pour un utilisateur non autorisé – le RedirectResult proviendra effectivement de la route et non de votre méthode de contrôleur.

Les bonnes nouvelles sont – il y a déjà une couverture de test pour ceci (dans le cadre du code source de cadre MVC), donc je dirais que vous n’avez pas besoin de vous en soucier; Assurez-vous simplement que votre méthode de contrôleur fait ce qu’il faut quand elle est appelée et faites confiance au framework pour ne pas l’appeler dans de mauvaises circonstances.

EDIT: Si vous voulez vérifier la présence de l’atsortingbut dans vos tests unitaires, vous devrez utiliser la reflection pour inspecter vos méthodes de contrôleur comme suit. Cet exemple vérifie la présence de l’atsortingbut Authorize sur la méthode ChangePassword POST dans la démo «Nouveau projet ASP.NET MVC 2» installée avec MVC2.

 [TestFixture] public class AccountControllerTests { [Test] public void Verify_ChangePassword_Method_Is_Decorated_With_Authorize_Atsortingbute() { var controller = new AccountController(); var type = controller.GetType(); var methodInfo = type.GetMethod("ChangePassword", new Type[] { typeof(ChangePasswordModel) }); var atsortingbutes = methodInfo.GetCustomAtsortingbutes(typeof(AuthorizeAtsortingbute), true); Assert.IsTrue(atsortingbutes.Any(), "No AuthorizeAtsortingbute found on ChangePassword(ChangePasswordModel model) method"); } } 

Eh bien, vous pourriez tester au mauvais niveau, mais c’est le test qui a du sens. Je veux dire, si je marque une méthode avec l’atsortingbut authorize (Roles = “Superhero”), je n’ai pas vraiment besoin d’un test si je l’ai signalé. Ce que je veux (je pense) est de tester qu’un utilisateur non autorisé n’a pas access et qu’un utilisateur autorisé le fait.

Pour un utilisateur non autorisé, un test comme celui-ci:

 // 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"); 

Eh bien, ce n’est pas facile et cela m’a pris 10 heures, mais la voici. J’espère que quelqu’un pourra en bénéficier ou me convaincre de faire un autre métier. 🙂 (BTW – J’utilise rhinocéros)

 [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(); } 

Bien que ce ne soit pas très utile sans cette fonction d’assistance:

 public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated) { var context = mocks.SsortingctMock(); var request = mocks.SsortingctMock(); var response = mocks.SsortingctMock(); var session = mocks.SsortingctMock(); var server = mocks.SsortingctMock(); var cachePolicy = mocks.Stub(); var user = mocks.SsortingctMock(); var identity = mocks.SsortingctMock(); var itemDictionary = new Dictionary(); 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; } 

Donc, vous obtenez la confirmation que les utilisateurs qui ne sont pas dans un rôle n’ont pas access. J’ai essayé d’écrire un test pour confirmer le contraire, mais après deux heures de plus à creuser à travers la plomberie mvc, je le laisserai aux testeurs manuels. (J’ai renfloué quand je suis arrivé à la classe VirtualPathProviderViewEngine. WTF? Je ne veux rien avoir à faire un VirtualPath ou un Provider ou ViewEngine beaucoup l’union des trois!)

Je suis curieux de savoir pourquoi c’est si difficile dans un cadre prétendument “testable”.

Pourquoi ne pas utiliser la reflection pour rechercher l’atsortingbut [Authorize] sur la classe de contrôleur et / ou la méthode d’action que vous testez? En supposant que le framework s’assure que l’atsortingbut est honoré, ce serait la chose la plus facile à faire.

Je ne suis pas d’accord avec la réponse de Dylan, car «l’utilisateur doit être connecté» n’implique pas que «la méthode du contrôleur est annotée avec AuthorizeAtsortingbute»

pour vous assurer que l’utilisateur doit être connecté lorsque vous appelez la méthode d’action, le framework ASP.NET MVC fait quelque chose comme ça (tenez simplement, cela finira par être plus simple)

 let $filters = All associated filter atsortingbutes 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 

Il est difficile d’implémenter ce pseudo script dans un code c # fonctionnel. Xania.AspNet.Simulator rend très simple la configuration d’un test comme celui-ci et effectue exactement ces étapes sous la couverture. Voici un exemple.

installez d’abord le paquet depuis nuget (version 1.4.0-beta4 au moment de l’écriture)

PM> install-package Xania.AspNet.Simulator -Pre

Ensuite, votre méthode de test pourrait ressembler à ceci (en supposant que NUnit et FluentAssertions soient installés):

 [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(); }