Search code examples
asp.net-mvc-3unit-testinghttpcontext

Need Help Writing Unit Test that on Method that requires HttpContext.Current.User


I am trying to write some unit tests for my MVC3 project (the first tests for this project) and I am getting stumped. Basically, I have a MemberQueries class that is used by my MemberController to handle all the logic.

I want to start writing tests on this class and want to start with a simple example. I have a method in this class called IsEditModeAvailable which determines if the user is a member of the "Site Administrator" role or that the user is able to edit their own data, but no one elses. I determine the last requirement by comparing the passed in Id value to the HttpContext User property.

The problem that I'm running into is I don't know how to mock or inject the proper parameters into my unit tests when creating the MemberQueries object. I am using, NUnit, Moq and Ninject, but I'm just not sure how to write the code. If I'm just not structuring this properly, please let me know as I'm a complete noob to unit testing.

Here's a sample of the code from my MemberQueries class:

public class MemberQueries : IMemberQueries
{
  private readonly IRepository<Member> _memberRepository;
  private readonly IMemberServices _memberServices;
  private readonly IPrincipal _currentUser;

  public MemberQueries(IUnitOfWork unitOfWork, IMemberServices memberServices, IPrincipal currentUser)
  {
    _memberRepository = unitOfWork.RepositoryFor<Member>();
    _memberServices = memberServices;
    _currentUser = currentUser;
  }

  public bool IsEditModeAvailable(int memberIdToEdit)
  {
    if (_currentUser.IsInRole("Site Administrator")) return true;
    if (MemberIsLoggedInUser(memberIdToEdit)) return true;
    return false;
  }

  public bool MemberIsLoggedInUser(int memberIdToEdit)
  {
    var loggedInUser = _memberServices.FindByEmail(_currentUser.Identity.Name);
    if (loggedInUser != null && loggedInUser.Id == memberIdToEdit) return true;
    return false;
  }

}

Here's a sample from my MemberServices class (which is in my domain project, referenced by MemberQueries):

public class MemberServices : IMemberServices
{
  private readonly IRepository<Member> _memberRepository;

  public MemberServices(IUnitOfWork unitOfWork)
  {
    _memberRepository = unitOfWork.RepositoryFor<Member>();
  }

  public Member Find(int id)
  {
    return _memberRepository.FindById(id);
  }

  public Member FindByEmail(string email)
  {
    return _memberRepository.Find(m => m.Email == email).SingleOrDefault();
  }
}

Finally, here's the stub of the unit test I am trying to write:

[Test]
public void LoggedInUserCanEditTheirOwnInformation()
{
  var unitOfWork = new UnitOfWork();

  var currentUser = new Mock<IPrincipal>();
  // I need to somehow tell Moq that the logged in user has a HttpContext.User.Name of "[email protected]"

  var memberServices = new Mock<MemberServices>();
  // I then need to tell Moq that it's FindByEmail("[email protected]") method should return a member with a UserId of 1

  var memberQueries = new MemberQueries(unitOfWork, memberServices.Object, currentUser.Object);

  // If the logged in user is "[email protected]" who has an Id of 1, then IsEditModeAvailable(1) should return true
  Assert.IsTrue(memberQueries.IsEditModeAvailable(1));
}

Solution

  • It looks like you are trying to test the MemberQueries.IsEditModeAvailable method. You have 2 cases to cover here. The Site Administrators case and the case where there's a currently logged user whose id matches the one passed as argument. And since the MemberQueries class relies purely on interfaces you could mock everything:

    [TestMethod]
    public void EditMode_Must_Be_Available_For_Site_Administrators()
    {
        // arrange
        var unitOfWork = new Mock<IUnitOfWork>();
        var currentUser = new Mock<IPrincipal>();
        currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(true);
        var memberServices = new Mock<IMemberServices>();
        var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object);
    
        // act
        var actual = memberQueries.IsEditModeAvailable(1);
    
        // assert
        Assert.IsTrue(actual);
    }
    
    [TestMethod]
    public void EditMode_Must_Be_Available_For_Logged_In_Users_If_His_Id_Matches()
    {
        // arrange
        var unitOfWork = new Mock<IUnitOfWork>();
        var currentUser = new Mock<IPrincipal>();
        var identity = new Mock<IIdentity>();
        identity.Setup(x => x.Name).Returns("[email protected]");
        currentUser.Setup(x => x.Identity).Returns(identity.Object);
        currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(false);
        var memberServices = new Mock<IMemberServices>();
        var member = new Member
        {
            Id = 1
        };
        memberServices.Setup(x => x.FindByEmail("[email protected]")).Returns(member);
        var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object);
    
        // act
        var actual = memberQueries.IsEditModeAvailable(1);
    
        // assert
        Assert.IsTrue(actual);
    }
    

    Actually there's a third case you need to cover: you have a currently logged in user, who is not a Site Administrator and whose id doesn't match the one passed as argument:

    [TestMethod]
    public void EditMode_Should_Not_Be_Available_For_Logged_In_Users_If_His_Id_Doesnt_Match()
    {
        // arrange
        var unitOfWork = new Mock<IUnitOfWork>();
        var currentUser = new Mock<IPrincipal>();
        var identity = new Mock<IIdentity>();
        identity.Setup(x => x.Name).Returns("[email protected]");
        currentUser.Setup(x => x.Identity).Returns(identity.Object);
        currentUser.Setup(x => x.IsInRole("Site Administrator")).Returns(false);
        var memberServices = new Mock<IMemberServices>();
        var member = new Member
        {
            Id = 2
        };
        memberServices.Setup(x => x.FindByEmail("[email protected]")).Returns(member);
        var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object);
    
        // act
        var actual = memberQueries.IsEditModeAvailable(1);
    
        // assert
        Assert.IsFalse(actual);
    }