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