我正在尝试对我的代码进行单元测试,其中包括以下行:
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");
但它给出了错误#34; NotSupportedException未被用户代码&#34;处理。我也尝试了以下内容:
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上找到的一些代码,但这会返回相同的错误&#34; NotSupportedException未被用户代码&#34;处理。
任何有关我如何进行的帮助将不胜感激。感谢。
答案 0 :(得分:36)
您无法直接模拟扩展方法,因此最好的方法是向下钻取,直到您点击扩展方法所依赖的可模拟的属性和方法。
在这种情况下,IIdentity.GetuUserId()
是一种扩展方法。我发布了它,但该库目前不是开源的,因此您必须亲自了解GetUserId()
取决于ClaimsIdentity.FindFirstValue()
。事实证明,这也是一种扩展方法,但它取决于标记为ClaimsIdentity.FindFirst()
的{{1}}。现在我们有了接缝,所以我们可以做到以下几点:
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"
(如Web API)。 MVC ApiControllers
类没有可设置的Controller
属性。幸运的是,通过User
&#39; 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
};
对象的所有内容是获取用户的ID,那么另一种适用于任何类型控制器的方法并且需要更少的代码:
User
现在,您只需拨打public class MyController: Controller //or ApiController
{
public Func<string> GetUserId; //For testing
public MyController()
{
GetUserId = () => User.Identity.GetUserId();
}
//controller actions
}
,而不是在想要用户的ID时调用User.Identity.GetUserId()
。当您需要在测试中模拟用户ID时,您只需这样做:
GetUserId()
两种方法都有利弊。第一种方法更彻底,更灵活,并使用两种控制器类型中已有的接缝,但它也有更多的代码,并且几乎没有读取。第二种方法更清晰,IMO更具表现力,但它是需要维护的额外代码,如果您不使用Func调用来获取用户的ID,您的测试将无法正常工作。选择哪一个更适合您的情况。
答案 1 :(得分: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");
有关详细信息,请参阅此处:http://msdn.microsoft.com/en-us/library/microsoft.aspnet.identity.identityextensions.getuserid(v=vs.111).aspx
答案 2 :(得分:4)
这对我有用
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);
现在,您可以从那些IIdentity扩展方法中获取值:
例如
var userid = _controller.User.Identity.GetUserId();
var userName = _controller.User.Identity.GetUserName();
使用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;
}
因此,GetUserId需要声明&#34; nameidentifier &#34;