Moq Roles.AddUserToRole测试

时间:2009-11-23 08:16:57

标签: asp.net-mvc unit-testing static moq

我正在使用Moq和MvcContrib TestHelper类为ASP.NET MVC 1.0中的项目编写单元测试。我遇到了一个问题。

当我来到我的AccountController中的Roles.AddUserToRole时,我得到一个System.NotSupportedException。 Roles类是静态的,Moq不能模拟静态类。

我该怎么办?

4 个答案:

答案 0 :(得分:7)

您可以使用像DI(依赖注入)这样的模式。在您的情况下,我会将RoleProvider传递给AccountController,默认情况下它将是默认的RoleProvider,并且是测试中的模拟对象。类似的东西:

public class AccountController
{
    private MembershipProvider _provider;
    private RoleProvider roleProvider;

    public AccountController()
      : this(null, null)
    {
    }

    public AccountController(MembershipProvider provider, RoleProvider roleProvider)
    {
      _provider = provider ?? Membership.Provider;
      this.roleProvider = roleProvider ?? System.Web.Security.Roles.Provider;
    }
}

MVC运行时将调用默认构造函数,而构造函数又将使用默认角色提供程序初始化AccountController。在单元测试中,您可以直接调用重载的构造函数,并传递MockRoleProvider(或使用Moq为您创建):

[Test]
public void AccountControllerTest()
{
    AccountController controller = new AccountController(new MockMembershipProvider(), new MockRoleProvider());
}

编辑:这就是我如何模拟整个HttpContext,包括主要用户。 要获得HttpContext的Moq版本:

public static HttpContextBase GetHttpContext(IPrincipal principal)
{
  var httpContext = new Mock<HttpContextBase>();
  var request = new Mock<HttpRequestBase>();
  var response = new Mock<HttpResponseBase>();
  var session = new Mock<HttpSessionStateBase>();
  var server = new Mock<HttpServerUtilityBase>();
  var user = principal;


  httpContext.Setup(ctx => ctx.Request).Returns(request.Object);
  httpContext.Setup(ctx => ctx.Response).Returns(response.Object);
  httpContext.Setup(ctx => ctx.Session).Returns(session.Object);
  httpContext.Setup(ctx => ctx.Server).Returns(server.Object);
  httpContext.Setup(ctx => ctx.User).Returns(user);

  return httpContext.Object;
}

Principal的模拟实现:

  public class MockPrincipal : IPrincipal
  {
    private IIdentity _identity;
    private readonly string[] _roles;

    public MockPrincipal(IIdentity identity, string[] roles)
    {
      _identity = identity;
      _roles = roles;
    }

    public IIdentity Identity
    {
      get { return _identity; }
      set { this._identity = value; }
    }

    public bool IsInRole(string role)
    {
      if (_roles == null)
        return false;
      return _roles.Contains(role);
    }
  }

MockIdentity:

public class MockIdentity : IIdentity
  {
    private readonly string _name;

    public MockIdentity(string userName)    {
      _name = userName;
    }

    public override string AuthenticationType
    {
      get { throw new System.NotImplementedException(); }
    }

    public override bool IsAuthenticated
    {
      get { return !String.IsNullOrEmpty(_name); }
    }

    public override string Name
    {
      get { return _name; }
    }
  }

魔术召唤:

MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));

请注意,我编辑了上面的代码,省略了一些自定义内容,但我确信这仍然有效。

答案 1 :(得分:0)

现在,当我尝试在ASP.NET MVC中测试ChangePassword()方法时,我遇到了另一个问题。

        try
        {
            if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword))
            {
                if (!TempData.ContainsKey("ChangePassword_success"))
                {
                    TempData.Add("ChangePassword_success", true);
                }

                return PartialView("ChangePassword");

            }

现在,当我到达此行时,我知道User为null。在我的测试类中,我有:

mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);

我认为这会起作用,但它并不关心我发送“johndoe”。如果我要模拟IPrincipal,那么User属性就是readonly。

答案 2 :(得分:0)

TypeMock Isolator嘲笑静力学等等。但是我第二次(和+1)Razzie的答案。

答案 3 :(得分:0)

我已完成您编码的内容,但我仍然认为用户在达到时为空

mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);

在我的Testclass中我有:

        //Arrange (Set up a scenario)
        var mockMembershipService = new Mock<IMembershipService>();
        MockIdentity identity = new MockIdentity("JohnDoe");
        var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
        var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
        string currentPassword = "qwerty";
        string newPassword = "123456";
        string confirmPassword = "123456";

        // Expectations

         mockMembershipService.Setup(pw => pw.MinPasswordLength).Returns(6);
         mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);

我是否用错误的参数调用我的cp.ChangePassword? MVCContrib Testhelpers类应该能够模拟Http上下文等吗?我找不到有关如何使用MVCContrib设置User.Identity.Name的信息。 我在教程中使用它来测试某些东西(模拟)会话:

        var builder = new TestControllerBuilder();
        var controller = new AccountController(mockFormsAuthentication.Object, mockMembershipService.Object, mockUserRepository.Object, null, mockBandRepository.Object);
        builder.InitializeController(controller);

编辑:我来得更远:

        MockIdentity identity = new MockIdentity("JohnDoe");
        var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
        var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
        controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);

但是我现在无法获得我的cp.ChangePassword期望返回true:

mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);

我发送“johndoe”字符串,因为它需要一个字符串作为User.Identity.Name的参数,但它不会返回true。