2

I've spent the better half of this afternoon trying to figure out how to mock (using MOQ) up the relevant bits and pieces to unit test the following HtmlHelper, which users the UrlHelper class to create img tags:

    public static IHtmlString Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes)
    {
        // Instantiate a UrlHelper
        var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

        // Create tag builder
        var builder = new TagBuilder("img");

        // Create valid id
        builder.GenerateId(id);

        // Add attributes
        builder.MergeAttribute("src", urlHelper.Content(url));
        builder.MergeAttribute("alt", alternateText);
        builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));

        // Render tag
        return new MvcHtmlString(builder.ToString(TagRenderMode.SelfClosing));
    }

Has anyone solved this problem already in their work?

4

2 回答 2

1

You should test the end result. If you know that you helper should rerurn something like, lets say:

<img src="test.jpg" id="testId" alt="something" />

Then your Assertion should be by comparing this string against the actual result. You should not care whether it is using helper internally itself or not.

于 2013-03-17T05:54:04.203 回答
1

When I run into .NET types which are not unit test friendly I usually write my own interface (with only the methods that I need) and a shim class implementing the interface which just forwards method/property calls to the original class. The shim is testable by inspection.

Then I write all of the code that I need to unit test against this interface (so I depend on abstractions, not concretions). This includes extension methods, which extend the interface rather than the original concrete type. In this respect, testing extension methods is as easy as new Mock<IHtmlHelper>() ... - you can make IHtmlHelper do anything you like because you have total control of its behaviour in your unit tests.

Typing out the interface and the shim is a little bit tedious for classes with larger interfaces, but mostly I find the overhead is very low. It also means that I end up doing fewer weird tricks to make things testable, so my code is more consistent and easy to follow (dependencies are always injected the same way, tests are always written the same way).

I think the point that Husein Roncevic is making is that you should be testing the clients of HtmlHelper in terms of observable state rather than behaviour. In other words, the unit tests for the client code should not be aware of which methods were called (extension methods or otherwise). It sounds like you want to unit test the extension method itself, which is a perfectly reasonable thing to do.

于 2013-03-18T17:42:50.883 回答