【问题标题】:Writing Unit Test for methods that use User.Identity.Name in ASP.NET Web API为在 ASP.NET Web API 中使用 User.Identity.Name 的方法编写单元测试
【发布时间】:2012-09-28 14:42:31
【问题描述】:

我正在使用 Unit Test for ASP.NET Web API 编写测试用例。

现在我有一个动作调用了我在服务层中定义的一些方法,我在其中使用了以下代码行。

string username = User.Identity.Name;
// do something with username
// return something

现在如何为此创建单元测试方法,我得到空引用异常。我对编写单元测试和东西有点陌生。

我只想为此使用单元测试。请帮我解决这个问题。

【问题讨论】:

  • 你是在测试动作方法还是服务层方法?

标签: asp.net .net unit-testing identity


【解决方案1】:

这是我的解决方案。

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, "Nikita"),
    new Claim(ClaimTypes.NameIdentifier, "1")
};

var identity = new ClaimsIdentity(claims);
IPrincipal user = new ClaimsPrincipal(identity);

controller.User = user;

【讨论】:

  • "无法将属性或索引器 'ControllerBase.User' 分配给 -- 它是只读的"
【解决方案2】:

如果您有很多控制器要测试,那么我建议创建一个基类并在构造函数中创建一个GenericIdentityGenericPrincipal 并设置Thread.CurrentPrincipal

GenericPrincipal principal = new GenericPrincipal(new 
    GenericIdentity("UserName"),null); Thread.CurrentPrincipal = principal; 

然后继承那个类..这样每个单元测试类都会有原理对象集

[TestClass]
public class BaseUnitTest
{
    public BaseUnitTest()
    {
      GenericPrincipal principal = new GenericPrincipal(new GenericIdentity("UserName"),null);   
      Thread.CurrentPrincipal = principal;
    }
}


[TestClass]
public class AdminUnitTest : BaseUnitTest
{
   [TestMethod]
   public void Admin_Application_GetAppliction()
   {
   }
}

【讨论】:

    【解决方案3】:

    当我运行单元测试时 - 在我的情况下,它使用 Windows 身份验证,Identity.Name 是我的域名,我也想为测试更改它。 所以我用such approach with 'hacking' things I want in IAuthenticationFilter

    【讨论】:

      【解决方案4】:

      这些都不适合我,我在另一个问题上使用了解决方案,该问题使用 Moq 在 ControllerContext 中设置用户名:https://stackoverflow.com/a/6752924/347455

      【讨论】:

        【解决方案5】:

        以下只是一种方法:

        public class FooController : ApiController {
        
            public string Get() {
        
                return User.Identity.Name;
            }
        }
        
        public class FooTest {
        
            [Fact]
            public void Foo() {
        
                var identity = new GenericIdentity("tugberk");
                Thread.CurrentPrincipal = new GenericPrincipal(identity, null);
                var controller = new FooController();
        
                Assert.Equal(controller.Get(), identity.Name);
            }
        }
        

        【讨论】:

        • 这是一个 xUnit 的东西。别担心。
        • 值得注意的是,此解决方案不适用于 MVC 控制器...仅适用于 API 控制器。
        • 只有 User.Identity.Name 工作正常,但 User.Identity.GetUserId() 方法却不行
        • @tugberk 如何在 mvc 控制器中达到同样的效果?
        【解决方案6】:

        在这里,我找到了另一种方法的解决方案,即如何通过测试方法为控制器级别测试设置用户身份名称。

        public static void SetUserIdentityName(string userId)
                {
                    IPrincipal principal = null;
                    principal = new GenericPrincipal(new GenericIdentity(userId), 
                    new string[0]);
                    Thread.CurrentPrincipal = principal;
                    if (HttpContext.Current != null)
                    {
                        HttpContext.Current.User = principal;
                    }
                }
        

        【讨论】:

          【解决方案7】:

          在 WebApi 5.0 中,情况略有不同。你现在可以这样做:

          controller.User = new ClaimsPrincipal(
            new GenericPrincipal(new GenericIdentity("user"), null));
          

          【讨论】:

          • 我在看System.Web.Mvc.dll中Controller的v5.2.0.0,而IPrincipal User只有get,没有set...
          【解决方案8】:

          这是我在NerdDinner 测试教程中找到的另一种方法。它适用于我的情况:

          DinnersController CreateDinnersControllerAs(string userName)
          {
          
              var mock = new Mock<ControllerContext>();
              mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
              mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
          
              var controller = CreateDinnersController();
              controller.ControllerContext = mock.Object;
          
              return controller;
          }
          
          [TestMethod]
          public void EditAction_Should_Return_EditView_When_ValidOwner()
          {
          
              // Arrange
              var controller = CreateDinnersControllerAs("SomeUser");
          
              // Act
              var result = controller.Edit(1) as ViewResult;
          
              // Assert
              Assert.IsInstanceOfType(result.ViewData.Model, typeof(DinnerFormViewModel));
          }
          

          请务必阅读完整部分:Mocking the User.Identity.Name property

          它使用 Moq 模拟框架,您可以使用 NuGet 将其安装在 Test project 中:http://nuget.org/packages/moq

          【讨论】:

          • 是的,我最近开始在我的新项目中使用 Moq,下次一定会考虑这种方法
          • 我刚刚写了这个答案,因为接受的答案在我的情况下不起作用。不知道为什么... :)
          • 如果我没记错的话,这个答案更适用于 MVC 而不是 Web API。可能是接受的答案对您不起作用的原因
          猜你喜欢
          • 2021-01-24
          • 1970-01-01
          • 2012-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-09
          • 1970-01-01
          • 2014-04-13
          相关资源
          最近更新 更多