44 votes

Mocking User.Identity en ASP.NET MVC

Je dois créer des tests unitaires pour un site Web ASP.NET MVC 2.0. Le site utilise l'authentification Windows.

Je me suis renseigné sur la nécessité de simuler le contexte HTTP pour le code qui utilise le HttpContext. J'ai l'impression que je commence à comprendre le modèle DI également. (Donner à la classe un attribut de type IRepository et ensuite passer dans un objet Repository lorsque vous instanciez le contrôleur).

Ce que je ne comprends pas, en revanche, c'est la manière correcte de simuler l'objet Windows Principal disponible via User.Identity. Cela fait-il partie du HttpContext ?

Quelqu'un a-t-il un lien vers un article qui démontre cela (ou une recommandation pour un livre) ?

Merci,

Trey Carroll

32voto

John Nelson Points 1997

J'ai utilisé IoC pour faire abstraction de cela avec un certain succès. J'ai d'abord défini une classe pour représenter l'utilisateur actuellement connecté :

public class CurrentUser
{
    public CurrentUser(IIdentity identity)
    {
        IsAuthenticated = identity.IsAuthenticated;
        DisplayName = identity.Name;

        var formsIdentity = identity as FormsIdentity;

        if (formsIdentity != null)
        {
            UserID = int.Parse(formsIdentity.Ticket.UserData);
        }
    }

    public string DisplayName { get; private set; }
    public bool IsAuthenticated { get; private set; }
    public int UserID { get; private set; }
}

Il faut un IIdentity dans le constructeur pour définir ses valeurs. Pour les tests unitaires, vous pouvez ajouter un autre constructeur pour vous permettre de contourner la méthode d'évaluation de l'utilisateur. IIdentity dépendance.

Ensuite, j'utilise Ninject (choisissez votre conteneur IoC préféré, peu importe), et je crée une liaison pour IIdentity comme tel :

Bind<IIdentity>().ToMethod(c => HttpContext.Current.User.Identity);

Ensuite, dans mon contrôleur, je déclare la dépendance dans le constructeur :

CurrentUser _currentUser;

public HomeController(CurrentUser currentUser)
{
    _currentUser = currentUser;
}

Le conteneur IoC voit que HomeController prend un CurrentUser et l'objet CurrentUser prend un IIdentity . Il résoudra les dépendances automatiquement, et voilà ! Votre contrôleur peut savoir qui est l'utilisateur actuellement connecté. Cela semble fonctionner assez bien pour moi avec FormsAuthentication. Vous pouvez peut-être adapter cet exemple à l'authentification Windows.

1 votes

Pour moi, cette méthode est préférable à celle qui consiste à simuler les contrôleurs, les principes et les contextes http, etc., car à chaque fois, j'oublie exactement quelles parties je dois simuler et je dois parcourir d'autres tests pour trouver le code approprié à copier et coller. Dans mon cas, les contrôleurs prennent une interface ICurrentUserResolver, qui a une méthode ResolveCurrentUser(), et le contrôleur n'a jamais besoin de se soucier de la façon dont l'utilisateur actuel est déterminé.

1 votes

Merci @JohnNelson, vous m'avez sauvé la vie.

19voto

MovGP0 Points 77

Je ne connais pas MVC 2.0, mais dans les versions plus récentes, vous pouvez simuler le ControllerContext :

// create mock principal
var mocks = new MockRepository(MockBehavior.Default);
Mock<IPrincipal> mockPrincipal = mocks.Create<IPrincipal>();
mockPrincipal.SetupGet(p => p.Identity.Name).Returns(userName);
mockPrincipal.Setup(p => p.IsInRole("User")).Returns(true);

// create mock controller context
var mockContext = new Mock<ControllerContext>();
mockContext.SetupGet(p => p.HttpContext.User).Returns(mockPrincipal.Object);
mockContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

// create controller
var controller = new MvcController() { ControllerContext = mock.Object };

voir aussi Comment tester unitairement une action de contrôleur MVC qui dépend de l'authentification en c# ?

10voto

Soe Moe Points 1650

Scott Hanselman montre dans son blog comment utiliser IPrincipal et ModelBinder pour faciliter le test du contrôleur en simulant IPrincipal.

5voto

Simon Points 432

Exemple de simulation de nom d'utilisateur et de SID sur MVC4. Le nom d'utilisateur et le SID (Windows Authentication) dans l'action suivante doivent être testés :

[Authorize]
public class UserController : Controller
{
    public ActionResult Index()
    {
        // get Username
        ViewBag.Username = User.Identity.Name;

        // get SID
        var lIdentity = HttpContext.User.Identity as WindowsIdentity;
        ViewBag.Sid = lIdentity.User.ToString();

        return View();
    }
}

J'utilise Moq y Outils de test de Visual Studio . Le test est mis en œuvre comme suit :

[TestMethod]
public void IndexTest()
{
    // Arrange
    var myController = new UserController();
    var contextMock = new Mock<ControllerContext>();
    var httpContextMock = new Mock<HttpContextBase>();
    var lWindowsIdentity = new WindowsIdentity("Administrator");

    httpContextMock.Setup(x => x.User).Returns(new WindowsPrincipal(lWindowsIdentity));

    contextMock.Setup(ctx => ctx.HttpContext).Returns(httpContextMock.Object);
    myController.ControllerContext = contextMock.Object;

    // Act
    var lResult = myController.Index() as ViewResult;

    // Assert
    Assert.IsTrue(lResult.ViewBag.Username == "Administrator");
    Assert.IsTrue(lResult.ViewBag.Sid == "Any SID Pattern");
}

1voto

Marcos Lima Points 48

J'ai modifié l'environnement dev global.asax et Web.Config pour utiliser FormsAuth pour forcer un utilisateur spécifique. Le nom d'utilisateur utilise le même format que celui de WindowsAuth. Voir :

public override void Init()
    {
        base.Init();

        this.PostAuthenticateRequest += 
             new EventHandler(MvcApplication_PostAuthenticateRequest);
    }

    void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
    {
        FormsAuthentication.SetAuthCookie("Domain\\login", true);
    }

L'Auth Windows ou Forms partage les mêmes modèles de connexion .

L'application fonctionnera à la fois avec l'authentification Windows et l'authentification Form.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X