2014-05-10 2 views
0

테스트중인 내 컨트롤러의 세션에 값을 저장하고 있습니다. 세션을 조롱하는 방법에 대한 여러 기사를 읽었으며 Milox의 답변을 Setting the httpcontext current session in unit test에 구현하려고합니다. 그러나 Locals | this | base | HttpContextSessions으로 드릴링 할 때 세션 변수를 설정할 때 Null 참조 예외로 테스트가 실패합니다. HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;MVC 5에서 조롱 세션이 작동하지 않습니다.

이것이 작업 코드입니다. vM.BusAcnt.Id이 유효한 int을 반환하고 int 값으로 대체하면 Session이 null이므로 테스트가 실패하므로 값을 저장할 수 없습니다.

MVC5, EF6 및 최신 버전의 xUnit, Moq 및 Resharper 테스트 러너를 사용하고 있습니다.

작업 :

public ActionResult Details(int id) 
{ 
    var vM = new BusAcntVm(); 
    vM.BusAcnt = _db.BusAcnts.FirstOrDefault(bA => bA.Id == id); 

    if ((User.IsInRole("Admin"))) return RedirectToAction("Action"); 

    HttpContext.Session["BsAcId"] = vM.BusAcnt.Id; 

    return View(vM); 
} 

MockHelpers :

public static class MockHelpers 
    { 
    public static HttpContext FakeHttpContext() 
    { 
     var httpRequest = new HttpRequest("", "http://localhost/", ""); 
     var stringWriter = new StringWriter(); 
     var httpResponce = new HttpResponse(stringWriter); 
     var httpContext = new HttpContext(httpRequest, httpResponce); 

     var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(), 
               new HttpStaticObjectsCollection(), 10, true, 
               HttpCookieMode.AutoDetect, 
               SessionStateMode.InProc, false); 

     httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
            BindingFlags.NonPublic | BindingFlags.Instance, 
            null, CallingConventions.Standard, 
            new[] { typeof(HttpSessionStateContainer) }, 
            null) 
          .Invoke(new object[] { sessionContainer }); 

     return httpContext; 
    } 
    } 

테스트 :

[Fact] 
public void AdminGetBusAcntById() 
{ 
    HttpContext.Current = MockHelpers.FakeHttpContext(); 
    var mockMyDb = MockDbSetup.MockMyDb(); 
    var controller = new BusAcntController(mockMy.Object); 
    var controllerContextMock = new Mock<ControllerContext>(); 
    controllerContextMock.Setup(x => x.HttpContext.User. 
    IsInRole(It.Is<string>(s => s.Equals("Admin")))).Returns(true); 
    controller.ControllerContext = controllerContextMock.Object; 

    var viewResult = controller.Details(1) as ViewResult; 
    var model = viewResult.Model as BusAcntVm; 

    Assert.NotNull(model); 
    Assert.Equal("Company 1", model.CmpnyName); 
} 

Milox의 코드를 이해하는 것 같다하지만이 동작하지 않습니다. 내가 놓친 것이 있습니까? 이 코드를 위반하는 MVC5의 변경 사항이 있습니까?

해결책 : 대린의 답변

구현. 이제는 값을 쓰는 세션이 있습니다 (값은 실제로 쓰여지지 않지만 테스트 목적에는 필요하지 않습니다). 테스트가 통과됩니다.

테스트 : 당신의 단위 테스트에서

[Fact] 
public void AdminGetBusAcntById() 
{ 
    var mockMyDb = MockDbSetup.MockMyDb(); 
    var controller = new BusAcntController(mockMy.Object); 
    var context = new Mock<HttpContextBase>(); 
    var session = new Mock<HttpSessionStateBase>(); 
    var user = new GenericPrincipal(new GenericIdentity("fakeUser"), new[] { "Admin" }); 
    context.Setup(x => x.User).Returns(user); 
    context.Setup(x => x.Session).Returns(session.Object); 
    var requestContext = new RequestContext(context.Object, new RouteData()); 
    controller.ControllerContext = new ControllerContext(requestContext, controller); 


    var viewResult = controller.Details(1) as ViewResult; 
    var model = viewResult.Model as BusAcntVm; 

    Assert.NotNull(model); 
    Assert.Equal("Company 1", model.CmpnyName); 
} 

답변

3

당신이 HttpContext.Current = MockHelpers.FakeHttpContext(); 설정하지만, ASP.NET MVC는 모두이 정적 속성을 사용하지 않습니다. ASP.NET MVC에서 HttpContext.Current을 잊어 버리십시오. 레거시 및 유닛 테스트가 비우호적 인 경우 (예, 귀하의 경우 유닛 테스트에서만 사용하고 있지만 ASP.NET MVC는이를 사용하지 않으므로 코드가 작동하지 않습니다).

요점은 ASP.NET MVC가 HttpContextBase, HttpRequestBase, HttpResponseBase, HttpSessionStateBase 등과 같은 추상화 작업을한다는 것입니다. 단위 테스트에서 쉽게 모의 할 수 있습니다.

의이 예 컨트롤러 보자

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     if ((this.User.IsInRole("Admin"))) 
     { 
      return RedirectToAction("Action"); 
     } 

     this.HttpContext.Session["foo"] = "bar"; 

     return View(); 
    } 
} 

을하는 방법과 해당 단위 테스트를 사용하여 필요한 추상화를 조롱에 의해처럼 보일 수 있습니다 MOQ에게 :

// arrange 
var controller = new HomeController(); 
var context = new Mock<HttpContextBase>(); 
var session = new Mock<HttpSessionStateBase>(); 
var user = new GenericPrincipal(new GenericIdentity("john"), new[] { "Contributor" }); 
context.Setup(x => x.User).Returns(user);    
context.Setup(x => x.Session).Returns(session.Object); 
var requestContext = new RequestContext(context.Object, new RouteData()); 
controller.ControllerContext = new ControllerContext(requestContext, controller); 

// act 
var actual = controller.Index(); 

// assert 
session.VerifySet(x => x["foo"] = "bar"); 
... 

을 그리고 당신이 원하는 경우 User.IsInRole("Admin") 조건을 입력 , 조롱 한 정체성에 적절한 역할을 제공하기 만하면됩니다.

+0

감사합니다. 너는 언제나처럼 그것을 못 박았 다. 내 테스트에서 구현을위한 OP : 해결 방법을 참조하십시오. – Joe

+0

안녕하세요 대린, 당신은 'ASP.NET MVC에서 HttpContext.Current를 잊어 버렸습니다.'라고 말하면서, 예를 들어 컨트롤러 외부의 사용자 정의 클래스에서 Session 객체에 어떻게 액세스 할 수 있습니까? 예를 들어 컨트롤러의 외부에서는 무엇이 이에 해당 하는가? HttpContext.Current.Session [ "MyField"] – Anthony

0

방법은 MOQ를 사용하여 세션의 조롱을 적용하는 방법은 다음과 같습니다.

저는 UnitTests Project에 기본 클래스를 만듭니다.구조는 다음과 같습니다.

[TestFixture] 
public class BaseClass 
{ 
    public Mock<ControllerContext> controllerContext; 
    public Mock<HttpContextBase> contextBase; 

    public BaseClass() 
    { 
     controllerContext = new Mock<ControllerContext>(); 
     contextBase = new Mock<HttpContextBase>(); 

     controllerContext.Setup(x => x.HttpContext).Returns(contextBase.Object); 
     controllerContext.Setup(cc => cc.HttpContext.Session["UserId"]).Returns(1); 
    } 
} 

다음을 참조하십시오. 요구 사항에 따라 변경할 수 있습니다.

쉽게 참조 할 수 있도록 TestClass의 이름을 "ControllerClassTest"로 지정합니다. 그래서 그런 다음이

[TestFixture] 
class ControllerClassTest : BaseClass 
{ 
} 

같은 BaseClass로와 ControllerClassTest을 상속하는 것, 내 테스트 클래스에서, 나는 우리가 선언하고 초기화 할 필요가 있음을 잊지 않기 위해이

[SetUp] 
public void Setup() 
{ 
    controller.ControllerContext = controllerContext.Object; 
} 

같은 설치 방법 내에서 ControllerContext을 초기화 할 것 컨트롤러를 먼저 누릅니다.

희망, 도움이됩니다.

관련 문제