Moq Roles.AddUserToRole 测试

发布于 2024-08-11 23:45:05 字数 209 浏览 4 评论 0原文

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

当我进入 AccountController 中的 Roles.AddUserToRole 时,我收到 System.NotSupportedException。 Roles 类是静态的,Moq 无法模拟静态类。

我能做些什么?

I am writing unit tests for a project in ASP.NET MVC 1.0 using Moq and MvcContrib TestHelper classes. I have run into a problem.

When I come to Roles.AddUserToRole in my AccountController, I get a System.NotSupportedException. The Roles class is static and Moq cannot mock a static class.

What can I do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

-柠檬树下少年和吉他 2024-08-18 23:45:05

您可以使用像 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);
    }
  }

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

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

You could use a pattern like DI (Dependency Injection). In your case, I would pass a RoleProvider to the AccountController, which would be the default RoleProvider by default, and a mock object in your tests. Something like:

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

The MVC runtime will call the default constructor, which in turn will initialize the AccountController with the default role provider. In your unit test, you can directly call the overloaded constructor, and pass a MockRoleProvider (or use Moq to create it for you):

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

EDIT: And here's how I mocked the entire HttpContext, including the principal user.
To get a Moq version of the HttpContext:

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

A mock implementation of 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);
    }
  }

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

And the magic call:

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

Note that I edited the code above to leave out some custom stuff, but I'm quite sure this should still work.

咿呀咿呀哟 2024-08-18 23:45:05

现在,当我尝试在 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 为空。在我的测试课中,我有:

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

我认为这会起作用,但它并不关心我发送“johndoe”。如果我要模拟 IPrincipal,则 User 属性是只读的。

Now I run into another problem when I try to test the ChangePassword() method in ASP.NET MVC.

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

                return PartialView("ChangePassword");

            }

Now I get that User is null, when I reach this line. In my testclass I have:

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

I thought that this would work, but it doesn't care for that I send "johndoe". And If I were to mock IPrincipal, the User property is readonly.

淡紫姑娘! 2024-08-18 23:45:05

TypeMock Isolator 会模拟静态等。但我赞同(并+1)Razzie 的答案。

TypeMock Isolator does mocking of statics etc. But I second (and +1'd) Razzie's answer.

预谋 2024-08-18 23:45:05

我已经完成了您编码的操作,但当它到达时,我仍然得到 User 为空:

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。

I have done what you coded, but I still get that User is null when it reaches:

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

In my Testclass I have:

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

Do I call my cp.ChangePassword with wrong parameters? And should MVCContrib Testhelpers classes be able to mock Http context and so on? I just can't find info for how to setup User.Identity.Name with MVCContrib.
I have used this from a tutorial to test something (mock) session:

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

EDIT: I have come a little further:

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

but my now I can't get my cp.ChangePassword in the expectation to return true:

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

I am sending "johndoe" string, because, it requires a string as a parameter for User.Identity.Name, but it doesn't return true.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文