Well... the problem is more complex, then the question title says. First of all, I have an extension method for HtmlHelper, which generates html link with parameters, based on current route parameters. Namely, if I am on page .../page?param1=val1¶m2=val2
, when I call my method ActionQuryLink
to generate link for example @Html.ActionQuryLink("link text", "action", new { param3 = "value3" })
I will get a link to <a href=".../page?param1=val1¶m2=val2¶m3=value3">link text</a>
. Well, the extension class itself is:
public static class ActionLinkHelper
{
public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action)
{
return (ActionQueryLink(htmlHelper, linkText, action, null, null));
}
public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action, object routeValues)
{
/*line 16*/return (ActionQueryLink(htmlHelper, linkText, action, routeValues, null));
}
public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action, object routeValues, IDictionary<string, object> htmlAttributes)
{
var queryString = htmlHelper.ViewContext.HttpContext.Request.QueryString;
var newRoute = routeValues == null
? htmlHelper.ViewContext.RouteData.Values
: new RouteValueDictionary(routeValues);
foreach(string key in queryString.Keys)
{
if(!newRoute.ContainsKey(key))
newRoute.Add(key, queryString[key]);
}
/*line 32*/string generatedLink = HtmlHelper.GenerateLink(
htmlHelper.ViewContext.RequestContext,
htmlHelper.RouteCollection,
linkText,
null,
action,
null,
newRoute,
htmlAttributes);
return new MvcHtmlString(generatedLink);
}
}
The main problem is to test this extension method
my unit test looks like:
[TestClass]
public class ActionLinkHeplerTests
{
#region ActionQueryLink
[TestMethod]
public void ActionLinkHeplerShouldGenerateCorrectActionLink()
{
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Setup(c => c.Request.QueryString).Returns(new NameValueCollection { { "param1", "value1" } });
mockHttpContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/");
mockHttpContext.Setup(c => c.Request.ApplicationPath).Returns("~/");
mockHttpContext.Setup(c => c.Request.CurrentExecutionFilePath).Returns("~/");
var mockProductRepository = new Mock<IProductRepository>();
mockProductRepository.Setup(p => p.GetCategory(It.IsAny<string>())).Returns(new Category());
var mockSettings = new Mock<ISettings>();
var categoryController = new CategoryController(mockProductRepository.Object, mockSettings.Object);
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(e => e.ViewData).Returns(new ViewDataDictionary { { "action", "action" } });
var viewContext = new ViewContext
{
HttpContext = categoryController.HttpContext,
RequestContext = new RequestContext
{
HttpContext = mockHttpContext.Object,
RouteData = new RouteData()
}
};
var mockRouteHandler = new Mock<IRouteHandler>();
var helper = new HtmlHelper(viewContext, mockViewDataContainer.Object, new RouteCollection { { "action", new Route("controller/action", mockRouteHandler.Object) } });
var expected = new MvcHtmlString("");
/*line 51*/var actual = helper.ActionQueryLink("link text", "action", new {view = "list"});
Assert.AreEqual(expected, actual);
}
#endregion
}
And I get such exception:
Test method TestSite.UnitTests.Helpers.ActionLinkHeplerTests.ActionLinkHeplerShouldGenerateCorrectActionLink threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
and stack trace:
at System.Web.UI.Util.GetUrlWithApplicationPath(HttpContextBase context, String url)
at System.Web.Routing.RouteCollection.NormalizeVirtualPath(RequestContext requestContext, String virtualPath)
at System.Web.Routing.RouteCollection.GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
at System.Web.Mvc.RouteCollectionExtensions.GetVirtualPathForArea(RouteCollection routes, RequestContext requestContext, String name, RouteValueDictionary values, ref Boolean usingAreas)
at System.Web.Mvc.UrlHelper.GenerateUrl(String routeName, String actionName, String controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, Boolean includeImplicitMvcValues)
at System.Web.Mvc.UrlHelper.GenerateUrl(String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, Boolean includeImplicitMvcValues)
at System.Web.Mvc.HtmlHelper.GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes, Boolean includeImplicitMvcValues)
at System.Web.Mvc.HtmlHelper.GenerateLink(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes)
at System.Web.Mvc.HtmlHelper.GenerateLink(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes)
at Core.Helpers.ActionLinkHelper.ActionQueryLink(HtmlHelper htmlHelper, String linkText, String action, Object routeValues, IDictionary`2 htmlAttributes) in ActionLinkHelper.cs: line 32
at Core.Helpers.ActionLinkHelper.ActionQueryLink(HtmlHelper htmlHelper, String linkText, String action, Object routeValues) in ActionLinkHelper.cs: line 16
at TestSite.UnitTests.Helpers.ActionLinkHeplerTests.ActionLinkHeplerShouldGenerateCorrectActionLink() in ActionLinkHeplerTests.cs: line 51
Well, I'm really sorry for such batch of code.
But I'm working on this problem about 3 days. As You can see error occurs even not in some MVC library but at System.Web.UI.Util
. Even if I could find System.Web.UI.Util
sources and add it to my solution as one more project, I couldn't force MVC framework to use this project instead of System.Web.UI.Util
from global assembly cash. To be honest it is even very difficult to replace MVC from GAC to MVC's sources project in my solution because it is very complex, there a lot of dependencies, and when I tried to do that I got a lot of errors, and most of them was of external libraries which already use MVC assembly from global assembly cash. Also the most important thing is, that my helper method works fine in my project, it calls exceptions only while testing. So my suggestion is that testing conditions for helper are not full or probably wrong. Summary, my question is how can I mock correct conditions for my html helper extension method using Moq, or, maybe, is there some other problem?
Turned out that to test helpers that rely on routing information one need to mock following methods of RequestContext.HttpContext
:
RequestContext.HttpContext.Request.ApplicationPath
- should return something that resembles root path (i.e. @"/"
)RequestContext.HttpContext.Response.ApplyAppPathModifier
- can simply return its input argument: Sample:
request.Setup(r => r.ApplicationPath).Returns(@"/");
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>()))
.Returns((string s) => s);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With