views:

2648

answers:

3

Hello,

One of my controllers actions, one that is being called in an Ajax request, is returning an URL to the client side so it can do a redirection. I'm using Url.RouteUrl(..) and during my unit tests this fails since the Controller.Url parameter is not pre-filled.

I tried alot of things, among others attempting to stub UrlHelper (which failed), manually creating a UrlHelper with a RequestContext that has a stubbed HttpContextBase (which failed on a RouteCollection.GetUrlWithApplicationPath call), ...

I have searched Google but found virtually nothing on the subject. Am I doing something incredibly stupid using Url.RouteUrl in my Controller action? Is there an easier way?

To make it even worse, I'd like to be able to test the returned URL in my unit test - in fact I'm only interested in knowing it's redirecting to the right route, but since I'm returning an URL instead of a route, I would like to control the URL that is resolved (eg. by using a stubbed RouteCollection) - but I'll be happy to get my test passing to begin with.

Thanks in advance.

+22  A: 

Here is one of my tests (xUnit + Moq) just for similar case (using Url.RouteUrl in controller)

Hope this helps:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");

var context = new Mock<HttpContextBase>(MockBehavior.Strict);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var controller = new LinkbackController(dbF.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
eu-ge-ne
For the time being I went with a solution where I abstracted away calls to UrlHelper so I can intercept them. Thanks for your snippet however, it will save me alot of time figuring out how to correctly mock a Request/Response/ControllerContext.
efdee
Thanks for the answer @eu-ge-ne, it helped me out a lot too. I've included some more moq setups to use a formcollection parameter used by UpdateModel
woopstash
+1 excellent. Though a tip: I use this as a MockHelper and change the response.Setup for ApplyAppPathModifier to this: response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny<String>())).Returns((String url) => url); It's ugly, but I get the serialized object back in url encoded form, instead of hardcoding the value returned.
eduncan911
+5  A: 

This post may be useful if you want to mock HttpContextBase class.

http://www.hanselman.com/blog/ASPNETMVCSessionAtMix08TDDAndMvcMockHelpers.aspx

Gerardo Contijoch
Cool, this helped me, though I had to add in some extra code to the method FakeHttpContext to stop the helper blowing up:context.Setup(ctx => ctx.Request.ApplicationPath).Returns("/AntiBlowup");I also refactored the code so it uses the newer Setup() syntax.Thanks.
RichardOD
A: 

Building off the answer by @eu-ge-ne which helped me a great deal:

I had an ActionResult that did a redirect as well as had an UpdateModel call with a FormCollection parameter. For the UpdateModel() to work I had to add this to my Mocked HttpRequestBase:

FormCollection collection = new FormCollection();
collection["KeyName"] = "KeyValue";

request.Setup(x => x.Form).Returns(collection);
request.Setup(x => x.QueryString).Returns(new NameValueCollection());

To test that the redirected URL was correct, you can do the following:

RedirectResult result = controller.ActionName(modelToSubmit, collection) as RedirectResult;
Assert.AreEqual("/Expected/URL", result.Url);
woopstash