2009-11-23 3 views
3

Moq 및 MvcContrib TestHelper 클래스를 사용하여 ASP.NET MVC 1.0의 프로젝트에 대한 단위 테스트를 작성하고 있습니다. 나는 문제가 생겼다.Moq Roles.AddUserToRole 테스트

내 AccountController에서 Roles.AddUserToRole을 방문하면 System.NotSupportedException이 발생합니다. Roles 클래스는 정적이고 Moq는 정적 클래스를 모의 할 수 없습니다.

어떻게해야합니까?

+0

Mo 응답을 업데이트하여 Moq을 사용하여 HttpContext를 설정하고 모의 HttpPrincipal 및 ID를 통합하는 방법을 확인했습니다. – Razzie

답변

7

DI (Dependency Injection)와 같은 패턴을 사용할 수 있습니다. 귀하의 경우에는 기본적으로 RoleProvider가 기본 RoleProvider가 될 AccountController와 테스트의 모의 객체에 전달됩니다. MVC의 런타임이 차례로 기본 역할 공급자와 AccountController를 초기화 할 기본 생성자를 호출합니다

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

:처럼 뭔가. 당신의 단위 테스트에서 직접 오버로드 된 생성자를 호출하고, MockRoleProvider을 통과 (또는 당신을 위해 그것을 만들 MOQ를 사용) 할 수 있습니다

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

편집을 : 나는 전체 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; 
} 

모의 구현 :

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)); 
는 HttpContext에의 MOQ 버전을 얻으려면

위의 코드를 편집하여 사용자 정의 항목을 생략했지만, 여전히 작동해야한다는 점을 유의하십시오.

+0

여전히 Roles.AddUserToRole에 대한 도움이 필요합니다. RoleProvider를 모의 해보면이 방법이 없습니다. AddUsersToRoles (문자열 [] 사용자 이름, 문자열 역할 []) 걸립니다. –

+0

그래,하지만 조롱 자체와는 아무런 관련이 없습니다. 'new string [] { "johndoe"} "를 첫 번째 매개 변수로 사용하여 AddUsersToRoles 메소드를 호출하거나 직접 메소드를 추가하십시오. Roles.AddUserToRole은 AddUsersToRoles를 호출하기 때문에 어쨌든 AddUsersToRoles는 아무 것도하지 않기 때문에 아무 것도하지 않을 것입니다. – Razzie

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

      } 

이제이 줄에 도달하면 해당 사용자가 null이됩니다.

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

내가이 일 것이라고 생각하지만, 그것에 대해 내가 "JohnDoe에"를 보내 상관하지 않습니다 내 TestClass에 나는있다. 그리고 IPrincipal을 모의한다면 User 속성은 읽기 전용입니다.

+0

HttpContext와 Principal 사용자를 조롱해야합니다. 사용자 속성은 읽기 전용 일 필요는 없지만 GenericIdentity 이름은 읽기 전용입니다. – Razzie

0

TypeMock 아이솔레이터는 통계학 등을 조롱합니다.하지만 저는 Razzie의 대답을 두 번째로 (+1)합니다.

+0

지금 나를 위해 TypeMock Isolator로 늦게 변경하십시오. MvcContrib이 IPrincipal 또는 User.Identity.Name을 설정하는 것을 모의하기 위해 뭔가를 할 수 있습니까? –

+0

업데이트 된 답변보기 – Razzie

+0

@ LenkoTB : BTW 나는 TMI를 권장하지 않았습니다. 저는 조롱 통계를 지원하는 유일한 조롱 프레임 워크라고 말했습니다. 일반적으로 정적에 의존하지 않는 것이 다른 이유 중의 하나입니다. –

0

난 당신이 코딩 무엇을했던,하지만 난 여전히 도달 할 때 사용자가 null 얻을 : 내가 가지고있는

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를 반환합니다 : 나는 "JohnDoe에"문자열을 보내고

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

, 그것은을위한 매개 변수로 문자열을 필요로하기 때문에 User.Identity.Name,하지만 true를 반환하지 않습니다.

+0

왜 그 라인에서 오류가 발생합니까? 거기에있는 사용자와 아무런 관계도 맺지 않았습니까? 아니면 "johndoe"를 User.Identity.Name으로 바꿨습니까? 어쨌든 User.Identity.Name은 사용할 수 없으므로 다음과 같이 바꿔야합니다. cp.ChangePassword (new MockIdentity ("JohnDoe"). 이름, currentPassword, newPassword) – Razzie

+0

아, 내가 네 대답을 받기 전에. 이제는 새로운 MockIdentity ("JohnDoe")를 보낼 때 작동합니다. 이름. 도움을 주셔서 감사 드리며 미안합니다. 나는 여전히 Moq에 익숙하지 않으며 ASP.NET MVC의 런타임에서 테스트하기가 어렵다. MVCContrib TestHelper 클래스는 도움이 될 것입니다. 그러나 그에 대한 많은 문서가 없습니다. 그래서 MVCContrib로 어떻게 할 것인지를 알아낼 때까지 HttpContext와 Identity에 대한 조롱을 고수 할 것입니다. –

+0

아무런 문제가 없으며 그들은 확실히 초보자 용 질문이 아닙니다. 나는 Moq 자신에게 처음이다. 그러나 MVCContrib의 TestHelper 클래스에 대한 지식이 없지만 Moq이 훌륭하다고 생각합니다. 그리고 공정하기 때문에, MVC에서 ASP.NET 런타임을 사용하는 것들을 테스트하는 것은 꽤 쉽습니다. 그러나, 쉽다! = 많은 일 :-) 다행이다! – Razzie