2009-09-14 95 views
0

我很新的測試和嘲笑,我想寫一個測試,以確保我的驗證邏輯正確設置ModelState錯誤。模擬MVC ControllerContext請求的問題

我所看到的是,controller.ControllerContext.HttpContext.Request設爲我第一次檢查,但在此之後,請求爲空每次。

這導致在MVC源* ValueProviderDictionary *類的PopulateDictionary方法一個空引用異常,因爲請求對象被訪問,該方法中多次而不確保請求不爲空。

我將幾種技術和幫手都粘在了一起,這些技巧和幫手是在研究如何克服迄今爲止遇到的一些問題的時候發現的,因此在這一點上我有點不確定我可能在哪裏介紹了該問題。

我在這裏使用模擬對象不正確嗎?

失敗的測試

//Test 
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel() 
{ 
    //Arrange 
    DataAccessFactoryMocks.MockAllDaos(); 

    var controller = new FooController(); 

    var testFormCollection = new NameValueCollection(); 
    testFormCollection.Add("foo.CustomerID", "3"); 
    testFormCollection.Add("_fooForm", SerializationUtils.Serialize(new FooModel())); 

    var mockHttpContext = new MockHttpContext(controller, "POST", testFormCollection, null); 

    //Accessor used to run the protected OnActionExecuting method in my controller 
    var accessor = new FooControllerAccessor(controller); 

    //Request is set, assertion passes 
    Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.Form); 

    //Request is null when accessing the property a second time, assertion fails 
    Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.QueryString); 

    //Act 
    accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>())); 

    //Assert 
    Assert.That(controller.ModelState.IsValid == false); 
} 

測試助手

//Test helper to create httpcontext and set controller context accordingly 
public class MockHttpContext 
{ 
    public HttpContextBase HttpContext { get; private set; } 
    public HttpRequestBase Request { get; private set; } 
    public HttpResponseBase Response { get; private set; } 
    public RouteData RouteData { get; private set; } 

    public MockHttpContext(Controller onController) 
    { 
     //Setup the common context components and their relationships 
     HttpContext = MockRepository.GenerateMock<HttpContextBase>(); 
     Request = MockRepository.GenerateMock<HttpRequestBase>(); 
     Response = MockRepository.GenerateMock<HttpResponseBase>(); 

     //Setup the context, request, response relationship 
     HttpContext.Stub(c => c.Request).Return(Request); 
     HttpContext.Stub(c => c.Response).Return(Response); 

     Request.Stub(r => r.Cookies).Return(new HttpCookieCollection()); 
     Response.Stub(r => r.Cookies).Return(new HttpCookieCollection()); 

     Request.Stub(r => r.QueryString).Return(new NameValueCollection()); 
     Request.Stub(r => r.Form).Return(new NameValueCollection()); 

     //Apply the context to the suppplied controller 
     var rc = new RequestContext(HttpContext, new RouteData()); 
     onController.ControllerContext = new ControllerContext(rc, onController); 
    } 

    public MockHttpContext(Controller onController, string httpRequestType, NameValueCollection form, NameValueCollection querystring) 
    { 
    //Setup the common context components and their relationships 
    HttpContext = MockRepository.GenerateMock<HttpContextBase>(); 
    Request = MockRepository.GenerateMock<HttpRequestBase>(); 
    Response = MockRepository.GenerateMock<HttpResponseBase>(); 

    //Setup request type based on parameter value 
    Request.Stub(r => r.RequestType).Return(httpRequestType); 

    //Setup the context, request, response relationship 
    HttpContext.Stub(c => c.Request).Return(Request); 
    HttpContext.Stub(c => c.Response).Return(Response); 

    Request.Stub(r => r.Cookies).Return(new HttpCookieCollection()); 
    Response.Stub(r => r.Cookies).Return(new HttpCookieCollection()); 

    Request.Stub(r => r.QueryString).Return(querystring); 
    Request.Stub(r => r.Form).Return(form); 

    //Apply the context to the suppplied controller 
    var rc = new RequestContext(HttpContext, new RouteData()); 
    onController.ControllerContext = new ControllerContext(rc, onController); 
    } 
} 

工作測試使用MvcContrib.TestHelper

public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel() 
    { 
     //Arrange 
     DataAccessFactoryMocks.MockAllDaos(); 

     TestControllerBuilder builder = new TestControllerBuilder(); 

     builder.Form.Add("fooModel.CustomerID", "3"); 

     builder.HttpContext.Request.Stub(r => r.RequestType).Return("POST"); 

     FooController controller = builder.CreateController<FooController>(); 

     var accessor = new FooControllerAccessor(controller); 

     //Act 
     accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>())); 

     //Assert 
     Assert.IsFalse(controller.ModelState.IsValid); 
    } 

回答

5

我建議你使用優秀的MVCContrib TestHelper來測試你的ASP.NET MVC控制器和Rhino Mocks。您會看到單元測試的大幅度簡化並提高了可讀性。

+0

謝謝,我會考慮他們,因爲我最終會喜歡爲每個測試消除所有這些設置。儘管如此,我仍然不明白爲什麼房產被清理,我想寫一些冗長的測試來幫助我理解發生的事情。 TestHelper似乎隱藏了很多我認爲有益於理解的內部工作。 – jjr2527 2009-09-14 17:28:32

+0

因爲當你定義期望時,你需要指定.Repeat.Any() – 2009-09-14 17:59:29

+0

我使用TestHelper工作。 .Repeat.Any()仍然沒有修復原始測試,所以我必須仍然做錯了什麼。我很滿意什麼出來。上面更新的代碼。 – jjr2527 2009-09-14 18:50:01

0

我從你的問題中瞭解到,ControllerContext的模擬也可以被存根對象所取代,因爲目標不是測試ControllerContext的行爲。另外,我也不知道爲什麼你需要一個FooControllerAccessor而你唯一關心的是斷言的ModelState,所以我把它出在這裏:

public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel() 
{ 
    // Arrange 
    var action = new FooController() 
     .Action("index") 
     .RequestData(new Dictionary<string, object>() 
     { 
      {"foo.CustomerID", 3}, 
      {"_fooForm", new FooModel()} 
     }); 

    //Act 
    var modelState = action.ValidateRequest(); 

    //Assert 
    Assert.That(modelState.IsValid == false); 
} 

要使用此代碼,您應該安裝Xania.AspNet.Simulator(在寫V1.4.0,beta5的時間)工程Mvc4和Mvc5

PM>安裝包Xania.AspNet。模擬器 - 預

更多的例子,請查看以下內容: