2013-06-24 38 views
1

這是故事。爲了能夠降合式引導控制到我的MVC形式,我建立具有單個命令生成以下結構的HtmlHelper擴展方法:嘲笑ViewContext來測試驗證錯誤消息

<div class="control-group"> 
    @Html.LabelFor(m => m.UserName, new { @class = "control-label" }) 
    <div class="controls"> 
     <div class="input-prepend"> 
      <span class="add-on"><i class="icon-user"></i></span> 
      @Html.TextBoxFor(m => m.UserName, new { @class = "input-xlarge" }) 
     </div> 
     @Html.ValidationMessageFor(m => m.UserName) 
    </div> 
</div> 

的方法本身並不難來寫。單元測試更困難。爲了使我的擴展方法可測試,我需要使用適當的模擬創建一個HtmlHelper<T>的實例。爲此,我調整了一箇舊的StackOverflow問題的答案,並拿出這個:

public static HtmlHelper<TModel> CreateHtmlHelper<TModel>(bool clientValidationEnabled, bool unobtrusiveJavascriptEnabled, ViewDataDictionary dictionary = null) 
{ 
    if (dictionary == null) 
     dictionary = new ViewDataDictionary { TemplateInfo = new TemplateInfo() }; 

    var mockViewContext = new Mock<ViewContext>(
     new ControllerContext(
      new Mock<HttpContextBase>().Object, 
      new RouteData(), 
      new Mock<ControllerBase>().Object), 
     new Mock<IView>().Object, 
     dictionary, 
     new TempDataDictionary(), 
     new Mock<TextWriter>().Object); 

    mockViewContext.SetupGet(c => c.UnobtrusiveJavaScriptEnabled).Returns(unobtrusiveJavascriptEnabled); 
    mockViewContext.SetupGet(c => c.FormContext).Returns(new FormContext { FormId = "myForm" }); 
    mockViewContext.SetupGet(c => c.ClientValidationEnabled).Returns(clientValidationEnabled); 
    mockViewContext.SetupGet(c => c.ViewData).Returns(dictionary); 
    var mockViewDataContainer = new Mock<IViewDataContainer>(); 
    mockViewDataContainer.Setup(v => v.ViewData).Returns(dictionary); 

    return new HtmlHelper<TModel>(mockViewContext.Object, mockViewDataContainer.Object); 
} 

到目前爲止好。

// ARRANGE 
ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider(); 
var helper = MvcMocks.CreateHtmlHelper<TestModel>(true, true); 
helper.ViewData.Model = new TestModel { Field = null }; 
helper.ViewData.ModelState.AddModelError("Field", "The field must be assigned."); 

// ACT 
var controlGroup = helper.ControlGroupFor(m => m.Field, CssClasses.IconUser).ToHtmlString(); 

這裏的問題是:現在,我可以創造一個HtmlHelper對象,如下所示我可以執行我的測試。在ControlGroupFor,其簽名是

public static HtmlString ControlGroupFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string iconClass) 

和我還沒有完成(是一個好一點的TDD開發者),我調用var validationMessage = html.ValidationMessageFor(expression)。儘管我使用了AddModelError,但ValidationMessageFor方法似乎認爲html.ViewData.ModelState["Field"]或者爲null,或者其ModelErrors集合爲空。我推斷這是因爲validationMessage

<span class="field-validation-valid" data-valmsg-for="Field" data-valmsg-replace="true"></span> 

,並根據ReSharper的,該ValidationMessageFor方法調用下來到這個方法:

private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes) 
    { 
     string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression); 
     FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation(); 

     if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null) 
     { 
      return null; 
     } 

     ModelState modelState = htmlHelper.ViewData.ModelState[modelName]; 
     ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors; 
     ModelError modelError = (((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0]); 

     if (modelError == null && formContext == null) 
     { 
      return null; 
     } 

     TagBuilder builder = new TagBuilder("span"); 
     builder.MergeAttributes(htmlAttributes); 
     builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName); 

     if (!String.IsNullOrEmpty(validationMessage)) 
     { 
      builder.SetInnerText(validationMessage); 
     } 
     else if (modelError != null) 
     { 
      builder.SetInnerText(GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState)); 
     } 

     if (formContext != null) 
     { 
      bool replaceValidationMessageContents = String.IsNullOrEmpty(validationMessage); 

      if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled) 
      { 
       builder.MergeAttribute("data-valmsg-for", modelName); 
       builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant()); 
      } 
      else 
      { 
       FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName); 
       // rules will already have been written to the metadata object 
       fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified 

       // client validation always requires an ID 
       builder.GenerateId(modelName + "_validationMessage"); 
       fieldMetadata.ValidationMessageId = builder.Attributes["id"]; 
      } 
     } 

     return builder.ToMvcHtmlString(TagRenderMode.Normal); 
    } 

現在,根據我所做的一切,在validationMessage應該給我一個span與一類field-validation-error和錯誤消息閱讀「該字段必須分配。」在我的觀察窗口中,html.ViewData.ModelState["Field"].Errors的計數爲1.我必須缺少一些東西。任何人都可以看到它是什麼?

+0

我已經取得了一些進展:儘管'mockViewContext'和'mockViewDataContainer'被模擬共享相同的'ViewData'屬性,我的監視窗口似乎認爲'html.ViewData'與'html不是同一個對象。 ViewContext.ViewData'。 –

回答

3

我修改了測試使用ViewContext.ViewData而不是ViewData直接:

// ARRANGE 
ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider(); 
var helper = MvcMocks.CreateHtmlHelper<TestModel>(true, true); 
helper.ViewContext.ViewData.Model = new TestModel { Field = null }; 
helper.ViewContext.ViewData.ModelState.AddModelError("Field", "The field must be assigned."); 

// ACT 
var controlGroup = helper.ControlGroupFor(m => m.Field, CssClasses.IconUser).ToHtmlString(); 

這已固定我的問題,但它仍然不是很清楚,我爲什麼helper.ViewContext.ViewDatahelper.ViewData應該是指向不同的情況下,給出的方法嘲笑成立了。