web-dev-qa-db-fra.com

Mocking User.Identity dans 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.

J'ai lu la nécessité de simuler le contexte HTTP pour le code qui traite du HttpContext. J'ai l'impression que je commence également à maîtriser le motif DI. (Attribuez à la classe un attribut de type IRepository, puis transmettez un objet Repository lorsque vous instanciez le contrôleur.) 

Ce que je ne comprends pas, cependant, est la manière appropriée de simuler l'objet Windows principal disponible via User.Identity. Est-ce que cela fait partie du HttpContext?

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

Merci,

Trey Carroll

37
Trey Carroll

J'ai utilisé IoC pour résumer ce problème 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 une 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 dépendance IIdentity.

Et puis j'utilise Ninject (choisissez votre conteneur IoC préféré, peu importe) et crée une liaison pour IIdentity en tant que telle:

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

Ensuite, à l'intérieur de 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 objet CurrentUser et que le constructeur CurrentUser prend un IIdentity. Il va résoudre les dépendances automatiquement, et le tour est joué! Votre contrôleur peut savoir qui est l'utilisateur actuellement connecté. Cela semble bien fonctionner pour moi avec FormsAuthentication. Vous pourrez peut-être adapter cet exemple à l'authentification Windows.

31
John Nelson

Je ne sais pas pour MVC 2.0, mais dans les versions plus récentes, vous pouvez vous moquer du 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 un-tester une action de contrôleur MVC qui dépend de l'authentification en c #?

18
MovGP0

Scott Hanselman shows dans son blog comment utiliser IPrincipal et ModelBinder pour faciliter le test du contrôleur en se moquant d'IPrincipal.

10
Soe Moe

Exemple pour le nom d'utilisateur moqueur et le SID sur MVC4. Le nom d'utilisateur et le SID (authentification Windows) de 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 et Outils de test Visual Studio . Le test est implémenté 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");
}
5
Simon

J'ai modifié l'environnement de développement global.asax et Web.Config pour utiliser FormsAuth pour forcer un utilisateur spécifique. Le nom d'utilisateur utilise le même format 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'authentification Windows ou Forms partage les mêmes modèles de connexion

L'application fonctionnera avec l'authentification Windows et l'authentification de formulaire.

1
Marcos Lima

Pour simuler WindowsIdentity, vous pouvez effectuer les opérations suivantes:

var mockedPrincipal = new Mock<WindowsPrincipal>(WindowsIdentity.GetCurrent());

mockedPrincipal.SetupGet(x => x.Identity.IsAuthenticated).Returns(true);
mockedPrincipal.SetupGet(x => x.Identity.Name).Returns("Domain\\User1");
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group1")).Returns(true);
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group2")).Returns(false);

puis utilisez mockedPrincipal.Object pour obtenir la WindowsIdentity réelle

0
Assaf S.