Mocking Controller.Url.Action(string, string, object, string) in ASP.NET MVC
Asked Answered
U

1

10

I use NUnit and Moq libraries for unit testing. I need to mock overloaded Url.Action(string, string, object, string), because my controller's action uses it to get an absolute url of an Action.

My try for now (look at MockUrlAction test):

[TestFixture]
public class ControllerTests   
{
    [Test]
    public void MockUrlAction()
    {
        var controller = new TestController();

        controller.Url = new UrlHelper(new RequestContext(MvcMoqHelpers.FakeHttpContext(), new RouteData()), GetRouteCollection());

        // it works
        Assert.AreEqual("/PathToAction", controller.Url.Action("TestAction")); 

        // but it doesn't work
        Assert.AreEqual("http://example.com/PathToAction", controller.Url.Action("TestAction", null, null, "http")); 
    }

    private RouteCollection GetRouteCollection()
    {
        BundleTable.MapPathMethod = MapBundleItemPath;
        var routes = new RouteCollection();
        RouteConfig.RegisterRoutes(routes);

        var adminArea = new AdminAreaRegistration();
        var adminAreaRegistrationContext = new AreaRegistrationContext(adminArea.AreaName, routes);
        adminArea.RegisterArea(adminAreaRegistrationContext);

        return routes;
    }
}

public static class MvcMoqHelpers
{
    public static HttpContextBase FakeHttpContext()
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();

        request.Setup(r => r.AppRelativeCurrentExecutionFilePath).Returns("/");
        request.Setup(r => r.ApplicationPath).Returns("/");
        response.Setup(s => s.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(s => s);

        context.Setup(ctx => ctx.Request).Returns(request.Object);
        context.Setup(ctx => ctx.Response).Returns(response.Object);
        context.Setup(ctx => ctx.Session).Returns(session.Object);
        context.Setup(ctx => ctx.Server).Returns(server.Object);

        return context.Object;
    }
}

And at the line

Assert.AreEqual("http://example.com/PathToAction", controller.Url.Action("TestAction", null, null, "http"));

I get an exception

System.NullReferenceException : Object reference not set to an instance of an object.
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.UrlHelper.Action(String actionName, String controllerName, Object routeValues, String protocol)

It's strange for me that controller.Url.Action("TestAction") works fine, but controller.Url.Action("TestAction", null, null, "http") does not.

P.S. MvcMoqHelpers.FakeHttpContext() is from here, maybe it will help to answer the question.

So the question is: how can I get Url.Action(string, string, object, string) mocked?

Thanks.

Ulberto answered 6/3, 2013 at 21:43 Comment(2)
What about URL in context.Request.SetupRequestUrl(url)?Sinatra
Possible duplicate of ASP.NET MVC: Mock controller.Url.ActionEar
S
10

You have to set Request.Url, and you have that piece of code in tutorial which you provided:

public static HttpContextBase FakeHttpContext(string url)
{
    HttpContextBase context = FakeHttpContext();
    context.Request.SetupRequestUrl(url);
    return context;
}

Reason is - in your Url.Action overload you do not provide hostname and protocol, so MVC tries to extract those values from Request.Url

if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName))
{
    Uri requestUrl = requestContext.HttpContext.Request.Url;
    protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp;
    hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host;
Sinatra answered 6/3, 2013 at 22:3 Comment(1)
Seems you are right. I modified method public static void SetupRequestUrl(this HttpRequestBase request, string url) from that tutorial by adding line mock.Setup(req => req.Url).Returns(new Uri(new Uri("http://localhost/"), url)); And it seems to work now if I call overloaded FakeHttpContext(string url). I am not sure if it's the simplest way to fix, but anyway I accept your answer. Thanks.Ulberto

© 2022 - 2024 — McMap. All rights reserved.