Unit Testing, how to set Thread.CurrentPrincipal and IsAuthenticated

6.8k views Asked by At

I am trying to perform a unit test of an n-tier application, service layer, repository layer and web api controllers. My repositories are checking the Thread.CurrentPrincipal object to get the current user. This is where I have a problem, when I create the implementation class that inherits from IPrincipal it does allow me to set the IsUserAuthenticated. Is there a way to simulate this for my unit tests. I would like to set the Thread.CurrentPrincipal to my implementation object. How would I simulate a user in this way?

In my RepositoryBase code, I call the following to determine if user is authenticated:

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

The actual test looks something like:

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

I am pulling IsUserAuthenticated from the UserPrincipal below:

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;
        }
    }
}
3

There are 3 answers

5
tkestowicz On BEST ANSWER

I would wrap Thread.CurrentPrincipal call with a class and then extract the interface. This approach would allow me to pass my dummy implementation as a dependency.

Another approach is to prepare static class like this:

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

}

Then you have to use ApplicationPrincipal.Current in your repositories instead of direct call. In your tests you will be able to switch default logic by calling ApplicationPrincipal.SwitchCurrentPrincipal(() => myPrincipal).

Edit:

I would change:

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

With:

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

And then in arrange section of the test I would change default IPrincipal with:

var principalMock = new UserPrincipal(isAuthenticated: true);

ApplicationPrincipal.SwitchCurrentPrincipal(() => principalMock);

I assume you can somehow overload the value of IsAuthenticated in UserPrincipal object.

0
gmlacrosse On

You might try using a object mocking framwork like NMock http://msdn.microsoft.com/en-us/magazine/cc163904.aspx

0
Ondřej On

Do not set principal using Thread.CurrentPrincipal in unit tests. Set it using AppDomain.CurrentDomain.SetThreadPrincipal. This way every thread in unit tests will inherit your principal.

Drawback is that you can set principal using this method only once.