Mocking User.Identity в ASP.NET MVC
Мне нужно создать Unit Tests для веб-сайта ASP.NET MVC 2.0. Сайт использует проверку подлинности Windows.
Я читал о необходимости издеваться над HTTP-контекстом для кода, который имеет дело с HttpContext. Я чувствую, что я начинаю обращаться с шаблоном DI. (Дайте классу атрибут типа IRepository, а затем передайте объект репозитория при создании экземпляра контроллера.)
Однако я не понимаю, что это правильный способ обмануть объект Principal Windows, доступный через User.Identity. Является ли эта часть HttpContext?
Есть ли у какого-либо органа ссылку на статью, которая демонстрирует это (или рекомендацию для книги)?
Спасибо,
Трей Кэрролл
Ответы
Ответ 1
Я использовал IoC, чтобы отвлечь это с некоторым успехом. Сначала я определил класс для представления текущего пользователя:
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; }
}
В конструкторе требуется IIdentity
установить его значения. Для модульных тестов вы можете добавить другой конструктор, чтобы вы могли обходить зависимость IIdentity
.
И затем я использую Ninject (выберите свой любимый контейнер IoC, не имеет значения) и создал привязку для IIdentity
как таковой:
Bind<IIdentity>().ToMethod(c => HttpContext.Current.User.Identity);
Затем внутри моего контроллера я объявляю зависимость в конструкторе:
CurrentUser _currentUser;
public HomeController(CurrentUser currentUser)
{
_currentUser = currentUser;
}
Контейнер IoC видит, что HomeController
принимает объект CurrentUser
, а конструктор CurrentUser
принимает IIdentity
. Он автоматически разрешит зависимости, и вуаля! Контроллер может узнать, кто из них в настоящий момент вошел в систему. Кажется, это работает очень хорошо для меня с помощью FormsAuthentication. Возможно, вы сможете адаптировать этот пример к проверке подлинности Windows.
Ответ 2
Я не знаю, для MVC 2.0, но в более новых версиях вы можете издеваться над 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 };
см. также Как выполнить модульное тестирование действия контроллера MVC, которое зависит от аутентификации в С#?
Ответ 3
Скотт Гензельман показывает в своем блоге, как использовать IPrincipal и ModelBinder, чтобы легче протестировать контроллер, насмехаясь над IPrincipal.
Ответ 4
Пример для издевательского имени пользователя и SID на MVC4.
Необходимо проверить имя пользователя и SID (Аутентификация Windows) в следующем действии:
[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();
}
}
Я использую Moq и Инструменты для тестирования Visual Studio. Тест выполняется следующим образом:
[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
Я изменил среду разработки global.asax и Web.Config для использования FormsAuth для принудительного использования определенного пользователя. Имя пользователя использует тот же формат WindowsAuth. См:
public override void Init()
{
base.Init();
this.PostAuthenticateRequest +=
new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie("Domain\\login", true);
}
Windows или Forms Auth используют те же шаблоны входа.
Приложение будет работать как с аутентификацией Windows, так и с проверкой подлинности.