需要帮助编写单元测试需要HttpContext.Current.User的方法

时间:2012-02-20 21:15:28

标签: asp.net-mvc-3 unit-testing httpcontext

我正在尝试为我的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));
}

2 个答案:

答案 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);