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
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.
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 #?
Scott Hanselman shows dans son blog comment utiliser IPrincipal et ModelBinder pour faciliter le test du contrôleur en se moquant d'IPrincipal.
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");
}
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.
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