Как мне высмеять User.Identity.GetUserId()?
Я пытаюсь unit test мой код, который включает в себя строку:
UserLoginInfo userIdentity = UserManager.GetLogins(User.Identity.GetUserId()).FirstOrDefault();
Я просто застрял на одном бите, поскольку я не могу получить:
User.Identity.GetUserId()
чтобы вернуть значение. Я пробовал следующее в настройке моего контроллера:
var mock = new Mock<ControllerContext>();
mock.Setup(p => p.HttpContext.User.Identity.GetUserId()).Returns("string");
Но он дает ошибку "NotSupportedException был необработанным кодом пользователя". Я также пробовал следующее:
ControllerContext controllerContext = new ControllerContext();
string username = "username";
string userid = Guid.NewGuid().ToString("N"); //could be a constant
List<Claim> claims = new List<Claim>{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", username),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userid)
};
var genericIdentity = new GenericIdentity("Andrew");
genericIdentity.AddClaims(claims);
var genericPrincipal = new GenericPrincipal(genericIdentity, new string[] { });
controllerContext.HttpContext.User = genericPrincipal;
На основе некоторого кода, который я нашел в stackoverflow, но это возвращает ту же ошибку "NotSupportedException был необработанным кодом пользователя".
Любая помощь относительно того, как я буду действовать, будет оценена по достоинству. Спасибо.
Ответы
Ответ 1
Вы не можете настроить метод GetUserId, потому что это метод расширения. Вместо этого вам нужно будет установить имя в свойстве пользователя iddentity. GetUserId будет использовать это, чтобы определить UserId.
var context = new Mock<HttpContextBase>();
var mockIdentity = new Mock<IIdentity>();
context.SetupGet(x => x.User.Identity).Returns(mockIdentity.Object);
mockIdentity.Setup(x => x.Name).Returns("test_name");
Смотрите это для получения дополнительной информации: http://msdn.microsoft.com/en-us/library/microsoft.aspnet.identity.identityextensions.getuserid(v=vs.111).aspx
Ответ 2
Вы не можете макетировать методы расширения напрямую, поэтому лучше всего развернуть до тех пор, пока вы не нажмете на свойства и методы, от которых зависит метод расширения, который является макетным.
В этом случае IIdentity.GetuUserId()
является методом расширения. Я бы опубликовал его, но библиотека в настоящее время не является открытым исходным кодом, поэтому вам нужно будет убедиться, что GetUserId()
зависит от ClaimsIdentity.FindFirstValue()
. Как оказалось, это также метод расширения, но он зависит от ClaimsIdentity.FindFirst()
, который помечен virtual
. Теперь у нас есть наш шов, поэтому мы можем сделать следующее:
var claim = new Claim("test", "IdOfYourChoosing");
var mockIdentity =
Mock.Of<ClaimsIdentity>(ci => ci.FindFirst(It.IsAny<string>()) == claim);
var controller = new MyController()
{
User = Mock.Of<IPrincipal>(ip => ip.Identity == mockIdentity)
};
controller.User.Identity.GetUserId(); //returns "IdOfYourChoosing"
Обновление: Я только понял, что решение, вышедшее выше, работает только для производного ApiControllers
(как, например, в Web API). Класс MVC Controller
не имеет настраиваемого свойства User
. К счастью, довольно легко пройти через Controller
ControllerContext
для достижения желаемого эффекта:
var claim = new Claim("test", "IdOfYourChoosing");
var mockIdentity =
Mock.Of<ClaimsIdentity>(ci => ci.FindFirst(It.IsAny<string>()) == claim);
var mockContext = Mock.Of<ControllerContext>(cc => cc.HttpContext.User == mockIdentity);
var controller = new MyController()
{
ControllerContext = mockContext
};
Однако, если все, что вы собираетесь использовать для объекта User
, - это получить идентификатор пользователя, есть другой подход, который будет работать для любого типа контроллера и требует намного меньше кода:
public class MyController: Controller //or ApiController
{
public Func<string> GetUserId; //For testing
public MyController()
{
GetUserId = () => User.Identity.GetUserId();
}
//controller actions
}
Теперь вместо вызова User.Identity.GetUserId()
, когда вам нужен идентификатор пользователя, вы просто вызываете GetUserId()
. Когда вам нужно издеваться над идентификатором пользователя в тестах, вы просто делаете это так:
controller = new MyController()
{
GetUserId = () => "IdOfYourChoosing"
};
Есть плюсы и минусы для обоих подходов. Первый подход является более тщательным и более гибким и использует швы, которые уже присутствуют на двух типах контроллеров, но также намного больше кода и почти не читаются. Второй подход намного более чист, и IMO более выразителен, но это дополнительный код для поддержки, и если вы не используете вызов Func для получения идентификатора пользователя, ваши тесты не будут работать. Выберите то, что лучше работает для вашей ситуации.
Ответ 3
Это сработало для меня
используя fakeiteasy
var identity = new GenericIdentity("TestUsername");
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "UserIdIWantReturned"));
var fakePrincipal = A.Fake<IPrincipal>();
A.CallTo(() => fakePrincipal.Identity).Returns(identity);
var _controller = new MyController
{
ControllerContext = A.Fake<ControllerContext>()
};
A.CallTo(() => _controller.ControllerContext.HttpContext.User).Returns(fakePrincipal);
Теперь вы можете получить значения из этих методов расширения iddentity:
например.
var userid = _controller.User.Identity.GetUserId();
var userName = _controller.User.Identity.GetUserName();
При просмотре метода GetUserId() с помощью ILSpy отображается
public static string GetUserId(this IIdentity identity)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;
if (claimsIdentity != null)
{
return claimsIdentity.FindFirstValue(http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier);
}
return null;
}
Таким образом, GetUserId нуждается в заявке " nameidentifier"