【问题标题】:Unit Testing, how to set Thread.CurrentPrincipal and IsAuthenticated单元测试,如何设置 Thread.CurrentPrincipal 和 IsAuthenticated
【发布时间】:2015-01-25 09:34:40
【问题描述】:

我正在尝试对 n 层应用程序、服务层、存储库层和 Web api 控制器执行单元测试。我的存储库正在检查 Thread.CurrentPrincipal 对象以获取当前用户。这是我遇到问题的地方,当我创建从 IPrincipal 继承的实现类时,它确实允许我设置 IsUserAuthenticated。有没有办法为我的单元测试模拟这个。我想将 Thread.CurrentPrincipal 设置为我的实现对象。我将如何以这种方式模拟用户?

在我的 RepositoryBase 代码中,我调用以下命令来确定用户是否已通过身份验证:

public bool IsUserAuthenticated
{
   get { return ((UserPrincipal)Principal).Identity.IsAuthenticated; }
}

实际测试如下:

Contract.Requires<UserAccessException>(IsUserAuthenticated, "Illegal Access.");

我正在从下面的 UserPrincipal 中提取 IsUserAuthenticated:

namespace HeyLetsTrain.Toolkit.Helper.Authentication
{
    public class UserPrincipal : IPrincipal
    {
        IIdentity _identity;
        string[] _role;
        string _emailAddress;


        public UserPrincipal(string name, string[] role)
        {
            if (role != null) 
            {
                _role = new string[role.Length];
                _role.CopyTo(role, 0);
                Array.Sort(_role);
            }
            _identity = new GenericIdentity(name);
        }

        public IIdentity Identity
        {
            get { return _identity; }
        }

        public string EmailAddress
        {
            get { return _emailAddress; }
            set { this._emailAddress = value; }
        }

        public bool IsInRole(string role)
        {
            return Array.BinarySearch(_role, role) >= 0 ? true : false;
        }

        public bool IsInAllRoles( params string [] roles )
        {
           foreach (string searchrole in roles )
           {
               if (Array.BinarySearch(_role, searchrole) < 0 )
               return false;
           }
           return true;
        }

        public bool IsInAnyRoles( params string [] roles )
        {
            foreach (string searchrole in roles )
            {
                if (Array.BinarySearch(_role, searchrole ) > 0 )
                return true;
            }
           return false;
        }
    }
}

【问题讨论】:

  • 在您提到的有关 Thread.CurrentPrincipal 的问题中,但在示例中您有 ((UserPrincipal)Principal)。这个 Principal 东西的起源是什么 - 它是你的 RepositoryBase 中的属性吗?
  • Principal 和 UserPrincipal 类型有哪些定义?
  • 有一个 Principal 属性,它返回 Thread.CurrentPrincipal 和 UserPrincipal,它是 IPrincipal 的一个实现。

标签: c# unit-testing iprincipal


【解决方案1】:

不要在单元测试中使用 Thread.CurrentPrincipal 设置主体。使用 AppDomain.CurrentDomain.SetThreadPrincipal 设置它。这样,单元测试中的每个线程都将继承您的主体。

缺点是这种方法只能设置一次principal。

【讨论】:

    【解决方案2】:

    我会用一个类包装 Thread.CurrentPrincipal 调用,然后提取接口。这种方法允许我将我的虚拟实现作为依赖项传递。

    另一种方法是像这样准备静态类:

    public static class ApplicationPrincipal
    {
      private static Func<IPrincipal> _current = () => Thread.CurrentPrincipal;
    
    
      public static IPrincipal Current
      {
          get { return _current(); }
      }
    
      public static void SwitchCurrentPrincipal(Func<IPrincipal> principal)
      {
          _current = principal;
      }
    
    }
    

    然后您必须在您的存储库中使用 ApplicationPrincipal.Current 而不是直接调用。在您的测试中,您将能够通过调用 ApplicationPrincipal.SwitchCurrentPrincipal(() => myPrincipal) 来切换默认逻辑。

    编辑:

    我会改变:

    public bool IsUserAuthenticated
    {
       get { return ((UserPrincipal)Principal).Identity.IsAuthenticated; }
    }
    

    与:

    public bool IsUserAuthenticated
    {
       get { return ApplicationPrincipal.Current.Identity.IsAuthenticated; }
    }
    

    然后在测试的安排部分,我将更改默认 IPrincipal 为:

    var principalMock = new UserPrincipal(isAuthenticated: true);
    
    ApplicationPrincipal.SwitchCurrentPrincipal(() => principalMock);
    

    我假设您可以以某种方式重载 UserPrincipal 对象中 IsAuthenticated 的值。

    【讨论】:

    • 我的一些代码取决于是否设置了 IsUserAuthenticated。如何模拟 IsUserAuthenticated 设置为 true?
    • 请说明您如何使用IsUserAuthenticated 对您的原始问题进行修改。
    • 正如@ScottChamberlain 提到的 - 给我们看一个例子,它会更容易提供帮助。
    • 我添加了上面的代码,有几个地方我正在测试这个属性。
    • 我在上面添加了 UserPrincipal 类。目前,它从 IIdentity 获取 IsAuthenticated 值。这个接口似乎需要一个 get 并且没有设置 IsAuthenticated。
    【解决方案3】:

    您可以尝试使用像 NMock 这样的对象模拟框架 http://msdn.microsoft.com/en-us/magazine/cc163904.aspx

    【讨论】: