Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing a Static Method of a Static Class

public static class ApplicationUtils
{
   public static bool IsCurrentUserAManager()
        {
            var username = WindowsIdentity.GetCurrent().Name;

            bool inAdmin;

            if (username == "AdminUser") {
               inAdmin = true;
            } else {
               inAdmin = false;
            }

            return inAdmin;
        }
  }

Above is some code that is used to test if the currently logged in user is an Admin, I want to unit test this section by passing in a different username and test if the result is correct.

I have heard that dependency injection would be the best way to do this. But I have no idea how to dependency inject into a static class and a static method.

Can anyone help me fill out the TestMethod below in order to pass in a username and test the method? (Not using enterprise)

[TestMethod]
public void IsCurrentUserAManagerTestIsAdmin()
{

}
like image 519
GeorgeB Avatar asked Mar 16 '18 10:03

GeorgeB


3 Answers

Refactor your class a little to take an identity as a parameter.

public static class ApplicationUtils
{
    public static bool IsUserAManager(IIdentity identity)
    {
        if (identity == null)
            throw new NullReferenceException("identity");


        return identity.Name == "AdminUser";
    }
}

And Your Test Class using Moq

[TestMethod]
public void IsUserAManagerTestIsAdminReturnsFalse()
{
    var mockedIdentity = new Moq.Mock<IIdentity>();
    mockedIdentity.Setup(x => x.Name).Returns("notanadmin");

    var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);

    Assert.IsFalse(result);
}

[TestMethod]
public void IsUserAManagerTestIsAdminReturnsTrue()
{
    var mockedIdentity = new Moq.Mock<IIdentity>();
    mockedIdentity.Setup(x => x.Name).Returns("AdminUser");

    var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);

    Assert.IsTrue(result);
}
like image 115
David Martin Avatar answered Nov 08 '22 20:11

David Martin


One should try to avoid coupling code to static classes as they are difficult to test.

That said, with your current code as is, it can be refactored to allow certain separations of concerns and a more fluent API via extension methods.

public static class ApplicationUtils {
    public static Func<IIdentity> userFactory = () => WindowsIdentity.GetCurrent();

    public static IIdentity CurrentUser { get { return userFactory(); } }

    public static bool IsManager(this IIdentity identity) {
        return identity != null && string.Compare(identity.Name, "AdminUser", true) == 0;
    }

    public static bool IsAuthenticated(this IIdentity identity) {
        return identity != null && identity.IsAuthenticated;
    }
}

The following test is used as an example to demonstrate how the above is used.

Moq was used as mocking framework

[TestMethod]
public void IsManager_Should_Return_True_For_AdminUser() {
    //Arrange
    var name = "AdminUser";
    var identity = Mock.Of<IIdentity>(_ => _.Name == name);
    ApplicationUtils.userFactory = () => identity;

    //Act
    var actual = ApplicationUtils.CurrentUser.IsManager();

    //Assert
    Assert.IsTrue(actual);
}

That done I would now like to suggest you refactor your code to make it more SOLID.

Abstract the functionality of getting the current user out into a service provider.

public interface IIdentityProvider {
    IIdentity CurrentUser { get; }
}

Pretty simple with an even simpler implementation.

public class DefaultIdentityProvider : IIdentityProvider {
    public IIdentity CurrentUser {
        get { return WindowsIdentity.GetCurrent(); }
    }
}

If using DI you can now inject the provider into dependent classes that have need to access the current user.

This allows the code to more flexible and maintainable as mocks/stubs of the provider and user can be used for isolated unit tests. The extension methods remain the same as they have very simple concerns.

Here is a simple example of a test for the extension method from earlier.

[TestMethod]
public void IsManager_Should_Return_True_For_AdminUser() {
    //Arrange
    var name = "AdminUser";
    var identity = Mock.Of<IIdentity>(_ => _.Name == name);
    var provider = Mock.Of<IIdentityProvider>(_ => _.CurrentUser == identity);

    //Act
    var actual = provider.CurrentUser.IsManager();

    //Assert
    Assert.IsTrue(actual);
}

Purely for demonstrative purposes, the test for the IsManager extension method only really needs an IIdentity to be exercised.

Assert.IsTrue(Mock.Of<IIdentity>(_ => _.Name == "AdminUser").IsManager());
like image 4
Nkosi Avatar answered Nov 08 '22 19:11

Nkosi


When your code is difficult to test, changing the code is a viable option! In this case, consider having the IsCurrentUserAManager receive the username as an input parameter (and rename it to IsUserAManager to reflect the change in behavior). It would look something like this:

public static class ApplicationUtils
{
   public static bool IsUserAManager(string username)
   {
      bool inAdmin;

      if (username == "AdminUser") {
         inAdmin = true;
      } else {
         inAdmin = false;
      }

      return inAdmin;
   }
}

This will allow you to send in different values for testing different scenarios. If however the entire class as it is cannot appear in the UT (due to environment constraints on initialization, for example), consider having just the business logic exported to a seperate non static class and write your UT for that.

like image 1
JonyVol Avatar answered Nov 08 '22 19:11

JonyVol