web-dev-qa-db-ja.com

User.Identity.GetUserId()をモックするにはどうすればよいですか?

私は次の行を含むコードをユニットテストしようとしています:

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はユーザーコードによって処理されませんでした」を返します。

私がどのように進めるかについての助けをいただければ幸いです。ありがとう。

30
AndrewPolland

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

20
Joe Taylor

拡張メソッドを直接モックすることはできません。そのため、拡張メソッドが依存する、モック可能なプロパティとメソッドに到達するまでドリルダウンすることをお勧めします。

この場合、IIdentity.GetuUserId()は拡張メソッドです。投稿しますが、ライブラリは現在オープンソースではないため、GetUserId()ClaimsIdentity.FindFirstValue()に依存していることを確認する必要があります。結局のところ、これは拡張メソッドでもありますが、virtualとマークされているClaimsIdentity.FindFirst()に依存しています。これで縫い目ができたので、次のことができます。

_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プロパティがありません。幸いなことに、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
};
_

ただし、Userオブジェクトを使用するのがユーザーのIDを取得することだけである場合は、どちらのタイプのコントローラーでも機能し、必要なコードがはるかに少ない別のアプローチがあります。

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

    public MyController()
    {
        GetUserId = () => User.Identity.GetUserId();
    }
    //controller actions
}
_

ユーザーのIDが必要なときにUser.Identity.GetUserId()を呼び出す代わりに、GetUserId()を呼び出すだけです。テストでユーザーIDをモックアウトする必要がある場合は、次のようにします。

_controller = new MyController()
{
    GetUserId = () => "IdOfYourChoosing"
};
_

両方のアプローチには長所と短所があります。最初のアプローチは、より徹底的で柔軟性が高く、2つのコントローラータイプに既に存在する継ぎ目を使用しますが、コードがはるかに多く、ほとんど読み込めません。 2番目のアプローチははるかに簡潔でIMOの表現力が豊かですが、維持するための余分なコードであり、Func呼び出しを使用してユーザーのIDを取得しないと、テストは機能しません。状況に合った方を選択してください。

37
joelmdev

これは私のために働いた

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); 

これらの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にはClaim "nameidentifier"が必要です

4
Haohmaru