使用NUnit对泛型htmlHelper方法进行单元测试

3

我是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
        );
    }

非常感谢您的帮助


你想要测试什么具体的行为? - Spock
也许只是一个简单的测试,看看是否可以返回 null? - Mohand Mokri
通过value.GetAttributeFrom<DescriptionAttribute>(value.ToString()).Description,其中value是一个泛型即TEnum。GetAttributeFrom是一个扩展方法,你需要指定要使用的泛型类型为<DescriptionAttribute>,在扩展方法中指定TEnum。你能编译这段代码吗?如果你能在问题中发布私有方法和任何扩展方法,那就太好了。这将更清楚地显示需要存根/模拟等的内容。 - Spock
嗨,Spock,这段代码可以编译。GetAttributeFrom的内容如下: public static class ExtensionMethods { public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute; }<DescriptionAttribute>来自命名空间System.ComponentModel.DescriptionAttribute。 - Mohand Mokri
谢谢@MohandMokri - 请查看下面的答案。 - Spock
1个回答

2
以下是如何编写此单元测试的方法。请注意,由于您没有指定使用模拟对象框架,因此我将使用贫困人士技术手写存根和模拟。如果您使用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; }
}

嗨 Spock,太好了!非常感谢你提供使用 moq 的替代方案。这非常有用,我相信其他人也会发现它的价值。 - Mohand Mokri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接