我如何模拟User.Identity.GetUserId()?

And*_*and 30 .net c# unit-testing moq asp.net-mvc-5

我正在尝试对我的代码进行单元测试,其中包括以下行:

UserLoginInfo userIdentity = UserManager.GetLogins(User.Identity.GetUserId()).FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)

因为我无法得到,我只是被困在一点上:

User.Identity.GetUserId()
Run Code Online (Sandbox Code Playgroud)

返回一个值.我一直在设置控制器时尝试以下操作:

var mock = new Mock<ControllerContext>();
mock.Setup(p => p.HttpContext.User.Identity.GetUserId()).Returns("string");
Run Code Online (Sandbox Code Playgroud)

但是它给出了"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;
Run Code Online (Sandbox Code Playgroud)

基于我在stackoverflow上找到的一些代码,但这会返回相同的错误"NotSupportedException未被用户代码处理".

任何有关我如何进行的帮助将不胜感激.谢谢.

joe*_*dev 38

您不能直接模拟扩展方法,因此最好的方法是向下钻取,直到您点击扩展方法所依赖的可模拟的属性和方法.

在这种情况下,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"
Run Code Online (Sandbox Code Playgroud)

更新:我刚刚意识到我上面发布的解决方案仅适用于派生ApiControllers(如Web API).MVC Controller类没有可设置的User属性.幸运的是这很容易通过进去ControllerControllerContext,以达到预期的效果:

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
};
Run Code Online (Sandbox Code Playgroud)

但是,如果你要使用的User对象是获取用户的Id,那么另一种方法可以适用于任何类型的控制器,并且需要的代码少得多:

public class MyController: Controller //or ApiController
{
    public Func<string> GetUserId; //For testing

    public MyController()
    {
        GetUserId = () => User.Identity.GetUserId();
    }
    //controller actions
}
Run Code Online (Sandbox Code Playgroud)

现在User.Identity.GetUserId(),您只需拨打电话,而不是在需要用户的ID时进行呼叫GetUserId().当你需要在测试中模拟用户ID时,你只需这样做:

controller = new MyController()
{
    GetUserId = () => "IdOfYourChoosing"
};
Run Code Online (Sandbox Code Playgroud)

两种方法都有利弊.第一种方法更彻底,更灵活,并且使用已经存在于两种控制器类型上的接缝,但它也有更多代码,并且几乎不能读取.第二种方法更清晰,IMO更具表现力,但它是额外的代码来维护,如果你不使用Func调用来获取用户的Id,你的测试将无法正常工作.选择哪一个更适合您的情况.


Joe*_*lor 20

您无法设置GetUserId方法,因为它是一种扩展方法.相反,您必须在用户的IIdentity属性上设置名称.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");
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅此内容:http://msdn.microsoft.com/en-us/library/microsoft.aspnet.identity.identityextensions.getuserid(v = vs111).aspx


Hao*_*aru 5

这对我有用

使用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); 
Run Code Online (Sandbox Code Playgroud)

现在您可以从这些 IIdentity 扩展方法中取回值:

例如。

        var userid = _controller.User.Identity.GetUserId();
        var userName = _controller.User.Identity.GetUserName();
Run Code Online (Sandbox Code Playgroud)

使用 ILSpy 查看 GetUserId() 方法时,它显示

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;
}
Run Code Online (Sandbox Code Playgroud)

所以 GetUserId 需要声明“ nameidentifier