2013-12-10 81 views
3

我是nUnit的新手,我的任務是爲某些htmlhelper擴展方法創建單元測試。單元測試nunit的通用htmlHelper方法

我該如何着手爲以下方法創建單元測試?

public static MvcHtmlString EnumDropDownListForOrderBy<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, bool orderById, string firstElement = null, object htmlAttributes = null) 
    { 
     ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 

     Type enumType = GetNonNullableModelType(metadata); 
     IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>(); 

     IEnumerable<SelectListItem> items = values.Select(value => new SelectListItem() 
     { 
      Text = value.GetAttributeFrom<DescriptionAttribute>(value.ToString()).Description, 
      Value = value.ToString(), 
      Selected = value.Equals(metadata.Model) 
     }); 

     IEnumerable<SelectListItem> itemsFiltered = items.Where(e => !string.IsNullOrEmpty(e.Text)).AsEnumerable(); 

     itemsFiltered = itemsFiltered.OrderBy(e => (orderById ? e.Text : e.Value)); 

     return htmlHelper.DropDownListFor(
      expression, 
      itemsFiltered, 
      firstElement, 
      htmlAttributes 
     ); 
    } 

任何幫助,將不勝感激

+0

要測試什麼具體的行爲? – Spock

+0

也許只是一個簡單的測試,看看是否可以返回null? –

+0

With value.GetAttributeFrom (value.ToString())。Description,value是一個泛型,即TEnum。而GetAttributeFrom是一個擴展方法指針,指定genric類型作爲用於指定擴展方法中的TEnum。你能編譯這段代碼嗎?如果您可以發佈您在問題中使用的私有方法和任何擴展方法,那就太好了。這給了很多關於什麼需要被扼殺/嘲笑的可見性。 – Spock

回答

2

下面是你如何寫這個單元測試。請注意,由於您沒有指定您使用Mock對象框架,因此我將使用窮人技術,即hand written stubs and mocks。如果您使用Moq,還有另一種輔助方法。

請務必注意,爲了簡化代碼執行,我對擴展方法進行了一些更改,因此測試不會意外失敗。無論如何,檢查任何意外的行爲是一個很好的防禦性編程練習。

回到測試。

SUT(測試系統)

這是SUT(待測系統)的樣子和支持類型的模樣。 (請相應隨意修改你的需要)

public static class MyHtmlHelper 
{ 
    public static MvcHtmlString EnumDropDownListForOrderBy<TModel, TEnum> 
     (this HtmlHelper<TModel> htmlHelper, 
     Expression<Func<TModel, TEnum>> expression, 
     bool orderById, string firstElement = null, object htmlAttributes = null, 
     Func<ModelMetadata> fromLambFunc = null) 
    { 
     ModelMetadata metadata = 
     ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);  
     Type enumType = GetNonNullableModelType(metadata); 
     IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>(); 

     IEnumerable<SelectListItem> items = 
      values.Select(value => new SelectListItem() 
     { 
      Text = GetText(value), 
      Value = value.ToString(), 
      Selected = value.Equals(metadata.Model) 
     }); 

     IEnumerable<SelectListItem> itemsFiltered = 
     items.Where(e => !string.IsNullOrEmpty(e.Text)).AsEnumerable(); 
     itemsFiltered = itemsFiltered.OrderBy(e => (orderById ? e.Text : e.Value)); 

     return htmlHelper.DropDownListFor 
     (expression, itemsFiltered, firstElement, htmlAttributes); 
    } 

    private static Type GetNonNullableModelType(ModelMetadata metadata) { 
     return typeof (SomeEnum); 
    } 

    private static string GetText<TEnum>(TEnum value) { 
     return value.GetAttributeFrom<DescriptionAttribute>(value.ToString()) != null 
      ? value.GetAttributeFrom<DescriptionAttribute>(value.ToString()).Description 
      : string.Empty; 
    } 
} 

public static class ExtensionMethodsAttr 
{ 
    public static T GetAttributeFrom<T>(this object instance, string propertyName) 
     where T : Attribute 
    { 
     var attrType = typeof(T); 
     var property = instance.GetType().GetProperty(propertyName); 

     return property != null ? 
     (T)property.GetCustomAttributes(attrType, false).First() : default(T) ; 
    } 
} 

public enum SomeEnum { A,} 

單元測試

[TestFixture] 
public class HtmlHelperTests 
{ 
    [Test] 
    public void EnumDropDownListForOrderBy_InvokeDropDownListFor_ReturnsExpectedSelectItemResult() 
    { 
     //Arrange 
     var expected = "<select id=\"Foo\" name=\"Foo\"></select>"; 
     var fakeHtmlHelper = CreateHtmlHelperStaticStubs 
     (new ViewDataDictionary(new FakeViewModel() {Foo = SomeEnum.A})); 
     //var fakeHtmlHelper = CreateHtmlHelperUsingMoq 
     (new ViewDataDictionary(new FakeViewModel(){Foo = SomeEnum.A})); 

     //Act 
     var result = fakeHtmlHelper.EnumDropDownListForOrderBy 
     (model => model.Foo, It.IsAny<bool>(), null, null, null); 

     //Assert 
     Assert.AreEqual(expected, result.ToString()); 
    } 


    private static HtmlHelper<FakeViewModel> 
     CreateHtmlHelperStaticStubs(ViewDataDictionary viewData) 
    { 
     var stubControllerContext = new ControllerContext(new FakeHttpContext(), new RouteData(), new FakeController()); 

     var stubViewContext = new ViewContext(stubControllerContext, new FakeView(), 
      new ViewDataDictionary(new FakeViewModel() { Foo = SomeEnum.A }), 
      new TempDataDictionary(), new TextMessageWriter()); 

     var fakeViewDataContainer = new FakeViewDataContainer(); 
     fakeViewDataContainer.ViewData = viewData; 

     return new HtmlHelper<FakeViewModel>(stubViewContext, fakeViewDataContainer); 
    } 

    //Moq version 
    private static HtmlHelper<FakeViewModel> 
     CreateHtmlHelperUsingMoq(ViewDataDictionary viewData) 
    { 
     var stubControllerContext = new Mock<ControllerContext>(); 
     stubControllerContext.Setup(x => x.HttpContext).Returns(new Mock<HttpContextBase>().Object); 
     stubControllerContext.Setup(x => x.RouteData).Returns(new RouteData()); 
     stubControllerContext.Setup(x => x.Controller).Returns(new Mock<ControllerBase>().Object); ; 

     var stubViewContext = new Mock<ViewContext>(); 
     stubViewContext.Setup(x => x.View).Returns(new Mock<IView>().Object); 
     stubViewContext.Setup(x => x.ViewData).Returns(viewData); 
     stubViewContext.Setup(x => x.TempData).Returns(new TempDataDictionary()); 

     var mockViewDataContainer = new Mock<IViewDataContainer>(); 

     mockViewDataContainer.Setup(v => v.ViewData).Returns(viewData); 

     return new HtmlHelper<FakeViewModel>(stubViewContext.Object, mockViewDataContainer.Object); 
    } 
} 


class FakeHttpContext : HttpContextBase 
{ 
    private Dictionary<object, object> _items = new Dictionary<object, object>(); 
    public override IDictionary Items { get { return _items; } } 
} 

class FakeViewDataContainer : IViewDataContainer 
{ 
    private ViewDataDictionary _viewData = new ViewDataDictionary(); 
    public ViewDataDictionary ViewData { get { return _viewData; } set { _viewData = value; } } 
} 

class FakeController : Controller { } 

class FakeView : IView 
{ 
    public void Render(ViewContext viewContext, System.IO.TextWriter writer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class FakeViewModel { 
    public SomeEnum Foo { get; set; } 
} 
+0

嗨Spock,太棒了!非常感謝您,也爲您提供使用moq的替代方案。這是非常有用的,我相信別人會發現它。 –