Mocking RouteData to test HtmlHelper RouteLink - c#

The helper:
public static MvcHtmlString RouteLink(this HtmlHelper helper, String linkText, String routeName, Object routeValues, String status)
{
if (status.ToLower() == "post-game" || status.ToLower() == "mid-game")
{
return helper.RouteLink(linkText, routeName, routeValues);
}
return MvcHtmlString.Create(" ");
}
The unit test:
[TestMethod]
public void RouteLinkTest()
{
var httpContext = new Mock<HttpContextBase>();
var routeData = new Mock<RouteData>();
var viewContext = new ViewContext { HttpContext = httpContext.Object, RouteData = routeData.Object };
var helper = new HtmlHelper(viewContext, new Mock<IViewDataContainer>().Object);
var target01 = helper.RouteLink("Linking Text", "route-name", new { id = "id" }, "status");
Assert.IsNotNull(target01);
}
The error:
Test method Web.Tests.Extensions.HtmlHelpersTest.RouteLinkTest threw exception:
System.ArgumentException: A route named 'route-name' could not be found in the route collection.
Parameter name: name
The question:
How do I mock the route to have the proper route name?

Instead of mockin the routevalues tell MvcApplication to register them
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
I use it when I have to test some redirectToAction in my controller with something like that
var controller = GetController();
var httpContext = Utilities.MockControllerContext(true, false).Object;
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
controller.Url = new UrlHelper(new RequestContext(httpContext, new RouteData()), routes);
var result = controller.DoMyStuff();
Assert.IsInstanceOfType(typeof(RedirectResult), result);
var actual = (RedirectResult)result;
Assert.AreEqual("/myurl", actual.Url.ToString());
I never tested an helper like yours but I think that should works

Related

Session null in unit test

I am trying to write some unit tests for fairly basic controller actions, in which I set Session["User"]. When I run the test, it is failing at this point with a null reference exception. Not entirely sure how to get round this.
Controller:
public ActionResult Index(int id)
{
Session["User"] = Id;
if (Id != 0)
{
return RedirectToAction("Home", "Home", new { Id});
}
return RedirectToAction("Register", "Home", new { Id});
}
Test:
[TestMethod]
public void NewUser_ShouldGoToRegister()
{
var controller = new HomeController();
HttpContext.Current = FakeHttpContext();
HttpContext.Current.Session.Add("User", "0");
var result = controller.Index(0) as ViewResult;
Assert.AreEqual("Register", result.ViewName);
}
public HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var httpResponce = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
HttpContext.Current = httpContext;
}
You can use some mocking tool like MOQ for mocking, to create fake HttpContext you can try like following code.
[TestMethod]
public void NewUser_ShouldGoToRegister()
{
var controller = new HomeController();
HttpContext.Current = FakeHttpContext();
HttpContext.Current.Session.Add("User", 1);
var result = controller.Index(0) as ViewResult;
Assert.AreEqual("Register", result.ViewName);
}
Your FakeHttpContext method should look like following code.
//Creating a Fake HTTP Context
// This is used for testing methods using Session Variables.
private HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://somethig.com/", "");
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection()
, 10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
If you have difficulties with this, this idea might help you overthinking it. Sometimes it's a good idea to create another layer of access to Session.
A simplified way would be to never directly access "Session", but rather have it wrapped into methods like this:
protected virtual string ReadFromSession(string key)
{
return Session[key];
}
protected virtual void StoreInSession(string key, string value)
{
Session[key] = value;
}
Everywhere in your Controller, you just use these methods instead of directly accessing Session. Easy enough.
Now the trick is that with Unit Test, you can override the Controller which you test:
public class MyControllerForTesting : MyController
{
private readonly IDictionary session;
public MyControllerForTesting(IDictionary session) : base()
{
this.session = session;
}
protected override string ReadFromSession(string key)
{
return this.session[key];
}
protected override void StoreInSession(string key, string value)
{
this.session[key] = value;
}
}
Now this might not work immediately as I didn't really test it but you get the Idea. Your unit tests work and you even can inspect the Dictionary if the values were set correctly etc.

mocking controller context and UrlHelper

inside unit test I'm faking http context using
var fakeHttpContext = new Mock<HttpContextBase>();
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(t => t.HttpContext).Returns(fakeHttpContext.Object);
this.controller.ControllerContext = controllerContext.Object;
now inside my code I'm using following
UrlHelper helper = new UrlHelper(this.ControllerContext.RequestContext);
string url = helper.Action("Details", "Pers", new { id = person.Id });
what should I mock inside unit test in order to use UrlHelper code?
Currently UrlHelper helper is null.
p.s. Because of clarity I did not show whole initialization of controller code inside test, it's working, but I'm struggling now with this request context and UrlHelper.
Controller already has a UrlHelper Url property that you can pass an instance or mock. no need to new one up in the code.
Take a look at this example test with a controller that uses the UrlHelper.
[TestClass]
public class UrlHelperTest {
[TestMethod]
public void MockUrlHelper() {
//Arrange
var requestUrl = new Uri("http://myrequesturl");
var request = Mock.Of<HttpRequestBase>();
var requestMock = Mock.Get(request);
requestMock.Setup(m => m.Url).Returns(requestUrl);
var httpcontext = Mock.Of<HttpContextBase>();
var httpcontextSetup = Mock.Get(httpcontext);
httpcontextSetup.Setup(m => m.Request).Returns(request);
var actionName = "MyTargetActionName";
var expectedUrl = "http://myfakeactionurl.com";
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper
.Setup(m => m.Action(actionName, "Register", It.IsAny<object>(), It.IsAny<string>()))
.Returns(expectedUrl)
.Verifiable();
var sut = new MyController();
sut.Url = mockUrlHelper.Object;
sut.ControllerContext = new ControllerContext {
Controller = sut,
HttpContext = httpcontext,
};
//Act
var result = sut.MyAction();
//Assert
mockUrlHelper.Verify();
}
public class MyController : Controller {
[HttpPost]
public ActionResult MyAction() {
var link = GenerateActionLink("MyTargetActionName", string.Empty, string.Empty);
return View((object)link);
}
private string GenerateActionLink(string actionName, string token, string username) {
string validationLink = null;
if (Request.Url != null) {
var encodedToken = EncodedUrlParameter(token);
var url = Url.Action(actionName, "Register", new { Token = encodedToken, Username = username }, Request.Url.Scheme);
validationLink = url;
}
return validationLink;
}
private string EncodedUrlParameter(string token) {
return "Fake encoding";
}
}
}

Unit Testing Asp.Net WebApi Controllers

I am trying to unit test an ApiController and set the current HttpContext, but every time I try and set the request information for the controller HttpContext.Current still seems to be null.
Here is the code:
[TestMethod]
public void MakePayment()
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/payment");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "payment" } });
var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<ApplicationUserManager>(mockUserStore.Object);
mockUserManager.Setup(x => x.GetUser(It.IsAny<string>())).Returns(new ApplicationUser());
IPayPalService payPalService = new PayPalService(mockUserManager.Object);
var paymentController = new PaymentController(payPalService);
paymentController.ControllerContext = new HttpControllerContext(config, routeData, request);
paymentController.Request = request;
paymentController.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
Payment payment = paymentController.DepositMoney(50.00);
Assert.AreEqual(payment.intent, "sale");
}
Is there anything that I am missing here?

Url.Link not working in WebAPI

Using the unit test below .. Iam trying to test my webapi.
[Test]
public void CheckControllerForCreate()
{
var config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
var controller = new ProductController
{
ControllerContext = new HttpControllerContext(config, routeData, request),
Request = request,
Url = new UrlHelper(request)
};
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
var result = controller.Create(new Product {Id = 4, Name = "Tomato Soup", Category = "Groceries", Price = 1});
}
[HttpPost]
public HttpResponseMessage Create(Product product)
{
var url = Url;
if (product == null)
throw new HttpResponseException(new HttpResponseMessage{StatusCode = HttpStatusCode.BadRequest,ReasonPhrase = "Product is not specified"});
products.Add(product);
var response = Request.CreateResponse(HttpStatusCode.Created, product);
string uri = Url.Link("Foo", product.Id);
response.Headers.Location = new Uri(uri);
return response;
}
The Create Action throws an exception because uri is null. Now, the Url helper is correctly picking up the RouteName , otherwise there would be a RouteName not found exception. I am assuming that somethign is wrong with my configuration.
I referred to http://www.peterprovost.org/blog/2012/06/16/unit-testing-asp-dot-net-web-api and several other posts for unit testing the controllers.
The WebAPI method is here on codeplex
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1acb241299a8#src/System.Web.Http/Routing/UrlHelper.cs
Edit
I have narrowed it down to vpd being null in ( UrlHelper)
IHttpVirtualPathData vpd = configuration.Routes.GetVirtualPath(
request: request,
name: routeName,
values: routeValues);
Can't seem to figure out why ?
You need to set the routeData into the request, the same way you did with the configuration:
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
Also you are incorrectly using the Url.Link helper. You haven't specified a controller nor you have indicated the id.
The code in your controller should look like this:
string uri = Url.Link("Foo", new { id = product.Id, controller = "product" });
UPDATE:
Here's a full example.
Controller:
public class ProductController : ApiController
{
public HttpResponseMessage Create(int id)
{
var uri = Url.Link("Foo", new { id = id, controller = "product" });
return Request.CreateResponse(HttpStatusCode.OK, uri);
}
}
Test:
// arrange
var config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary(new { controller = "product" }));
var controller = new ProductController
{
ControllerContext = new HttpControllerContext(config, routeData, request),
Request = request,
Url = new UrlHelper(request)
};
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
// act
var result = controller.Create(4);
// assert
...

URL helper method test failing when returning string

I am using ASP.NET MVC 3 and NUnit.
I created a helper method to to return an action method as such (overloaded method):
public static object CategoryIndex(this UrlHelper urlHelper)
{
return new { controller = "Category", action = "Index" };
}
public static string CategoryIndex(this UrlHelper helper, int categoryId)
{
return helper.RouteUrl(new { controller = "Category", action = "Index", id = categoryId });
}
The test that is failing is the second test called CategoryIndex_should_navigate_to_category_index_action_method_with_child_category_id().
private HttpContextBase httpContextBaseStub;
private RequestContext requestContext;
private UrlHelper urlHelper;
[SetUp]
public void SetUp()
{
httpContextBaseStub = MockRepository.GenerateStub<HttpContextBase>();
requestContext = new RequestContext(httpContextBaseStub, new RouteData());
urlHelper = new UrlHelper(requestContext);
}
[Test]
public void CategoryIndex_should_navigate_to_category_index_action_method()
{
// Act
object actual = UrlHelperNavigationExtensions.CategoryIndex(urlHelper);
// Assert
RouteValueDictionary routes = new RouteValueDictionary(actual);
Assert.AreEqual("Category", routes["controller"]);
Assert.AreEqual("Index", routes["action"]);
}
[Test]
public void CategoryIndex_should_navigate_to_category_index_action_method_with_child_category_id()
{
// Arrange
int childCategoryId = 1;
// Act
string actual = UrlHelperNavigationExtensions.CategoryIndex(urlHelper, childCategoryId);
// Assert
Assert.AreEqual("/Category/Index/1", actual);
}
It's complaining that actual is null. Why would this be and how would I rectify it?
it seems to me that you don't initialize the routecollection. I guess something like that would do the trick
[SetUp]
public void SetUp()
{
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
httpContextBaseStub = MockRepository.GenerateStub<HttpContextBase>();
requestContext = new RequestContext(httpContextBaseStub, new RouteData());
//urlHelper = new UrlHelper(requestContext);
urlHelper = new UrlHelper(requestContext, routes);
}
It seems also, that you don't setup the HttpContextBase. Be sure to have it properly mocked or you will get some null reference exception.
[SetUp]
public void SetUp()
{
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
//httpContextBaseStub = (new Moq.Mock<HttpContextBase>()).Object;
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((String url) => url);
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Setup(c => c.Request).Returns(request.Object);
mockHttpContext.Setup(c => c.Response).Returns(response.Object);
requestContext = new RequestContext(mockHttpContext.Object, new RouteData());
urlHelper = new UrlHelper(requestContext, routes);
}
My guess is because the route table is empty, so it doesn't know how to generate the url.
Add the normal /{controller}/{action} route and it should work.
(i'm on a phone so forgive if this is wrong)
You need to stub an IRouteHandler implementation, which I've called stub_handler here.
RouteTable.Routes.Add(new Route
(
"{controller}/{action}/{id}"
, stub_handler
);

Categories

Resources