我正在尝试为我的MVC3项目(这个项目的第一次测试)编写一些单元测试,我感到难过。基本上,我有一个 MemberQutroller 类,我的 MemberController 用它来处理所有逻辑。
我想开始在这个类上编写测试,并希望从一个简单的例子开始。我在这个类中有一个名为 IsEditModeAvailable 的方法,它确定用户是否是“站点管理员”角色的成员,或者用户是否能够编辑自己的数据,但没有人可以使用。我通过比较传递的Id值和HttpContext User属性来确定最后一个要求。
我遇到的问题是我在创建MemberQueries对象时不知道如何模拟或将适当的参数注入我的单元测试中。我正在使用 NUnit,Moq和Ninject ,但我只是不确定如何编写代码。如果我没有正确构建这个,请告诉我,因为我是一个完整的单元测试菜单。
以下是我的MemberQueries类的代码示例:
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;
}
}
以下是我的MemberServices类的示例(在我的域项目中,由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();
}
}
最后,这是我试图编写的单元测试的存根:
[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 "jdoe@acme.com"
var memberServices = new Mock<MemberServices>();
// I then need to tell Moq that it's FindByEmail("jdoe@acme.com") 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 "jdoe@acme.com" who has an Id of 1, then IsEditModeAvailable(1) should return true
Assert.IsTrue(memberQueries.IsEditModeAvailable(1));
}
答案 0 :(得分:3)
看起来您正在尝试测试MemberQueries.IsEditModeAvailable
方法。这里有2个案例。 Site Administrators
情况和当前登录用户的id匹配作为参数传递的用户的情况。由于MemberQueries
类纯粹依赖于接口,因此可以模拟所有内容:
[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("john.doe@gmail.com");
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("john.doe@gmail.com")).Returns(member);
var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object);
// act
var actual = memberQueries.IsEditModeAvailable(1);
// assert
Assert.IsTrue(actual);
}
实际上你需要覆盖第三种情况:你有一个当前登录的用户,他不是站点管理员,其id与作为参数传递的id不匹配:
[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("john.doe@gmail.com");
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("john.doe@gmail.com")).Returns(member);
var memberQueries = new MemberQueries(unitOfWork.Object, memberServices.Object, currentUser.Object);
// act
var actual = memberQueries.IsEditModeAvailable(1);
// assert
Assert.IsFalse(actual);
}
答案 1 :(得分:2)
好消息是,您将用户作为IPrincipal传递到需要它的代码中,而不是引用HttpContext.Current.User。您需要做的就是设置模拟IPrincipal,以便它返回该测试所需的值。
var mockIdentity = new Mock<IIdentity>();
mockIdentity.Setup(x => x.Name).Returns("joe@acme.com");
var mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(mockIdentity.Object);