Подтвердить что ты не робот

Как мне высмеять 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 был необработанным кодом пользователя".

Любая помощь относительно того, как я буду действовать, будет оценена по достоинству. Спасибо.

4b9b3361

Ответ 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"