Setting properties on a mocked HttpContextBase - asp.net-mvc

I'm working on an ASP.NET MVC application, and am trying to write some unit tests against controller actions, some of which manipulate properties on the HttpContext, such as Session, Request.Cookies, Response.Cookies, etc. I'm having some trouble figuring out how to "Arrange, Act, Assert"...I can see Arrange and Assert...but I'm having trouble figuring out how to "Act" on properties of a mocked HttpContextBase when all of its properties only have getters. I can't set anything on my mocked context from within my controller actions...so it doesn't seem very useful. I'm fairly new to mocking, so I'm sure there's something that I'm missing, but it seems logical to me that I should be able to create a mock object that I can use in the context of testing controller actions where I can actually set property values, and then later Assert that they're still what I set them to, or something like that. What am I missing?
public static HttpContextBase GetMockHttpContext()
{
var requestCookies = new Mock<HttpCookieCollection>();
var request = new Mock<HttpRequestBase>();
request.Setup(r => r.Cookies).Returns(requestCookies.Object);
request.Setup(r => r.Url).Returns(new Uri("http://example.org"));
var responseCookies = new Mock<HttpCookieCollection>();
var response = new Mock<HttpResponseBase>();
response.Setup(r => r.Cookies).Returns(responseCookies.Object);
var context = new Mock<HttpContextBase>();
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(new Mock<HttpSessionStateBase>().Object);
context.Setup(ctx => ctx.Server).Returns(new Mock<HttpServerUtilityBase>().Object);
context.Setup(ctx => ctx.User).Returns(GetMockMembershipUser());
context.Setup(ctx => ctx.User.Identity).Returns(context.Object.User.Identity);
context.Setup(ctx => ctx.Response.Output).Returns(new StringWriter());
return context.Object;
}

Not sure if anyone is interested but I have translated the Moq FakeHttpContext to one using Rhino Mocks (my weapon of choice).
public static HttpContextBase FakeHttpContext()
{
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
var request = MockRepository.GenerateMock<HttpRequestBase>();
var response = MockRepository.GenerateMock<HttpResponseBase>();
var session = MockRepository.GenerateMock<HttpSessionStateBase>();
var server = MockRepository.GenerateMock<HttpServerUtilityBase>();
var cookies = new HttpCookieCollection();
response.Stub(r => r.Cookies).Return(cookies);
request.Stub(r => r.Cookies).Return(cookies);
request.Stub(r => r.Url).Return(new Uri("http://test.com"));
httpContext.Stub(x => x.Server).Return(server);
httpContext.Stub(x => x.Session).Return(session);
httpContext.Stub(x => x.Request).Return(request);
httpContext.Stub(x => x.Response).Return(response);
var writer = new StringWriter();
var wr = new SimpleWorkerRequest("", "", "", "", writer);
System.Web.HttpContext.Current = new System.Web.HttpContext(wr);
return httpContext;
}
with a usage in a Unit Test of
_httpContext = FakeHttpContext();
var cookieManager = new CookieManager(_httpContext);
string username = cookieManager.GetUsername();
_httpContext.AssertWasCalled(hc => { var dummy = hc.Request; });

Hey, I think you're just experiencing a bit of a disconnect here, no big deal. What you describe is 100% possible.
I'm not entirely positive on why you can't set properties on your Mocks, but if you post the full code for your test I'd be happy to go through it with you. Just off the top of my head, I'll suggest two things:
There is a difference between Setup() and SetupProperty(). SetupProperty() is probably what you're after if you want to track values on properties, rather than just get a value from them once.
Alternately try calling SetupAllProperties() on any mock that you need to set a property on.
Check the Moq quickstart as well for some examples.

Related

Unit testing controllers using UserManager.FindById

I am new to unit testing .net applications and am having difficulty with what I would imagine to be a very simple case.
// GET: Entities
public ViewResult Index()
{
_User = UserManager.FindById(User.Identity.GetUserId());
return View(entityRepository.GetEntities(_User.entityId));
}
I want to test that the correct view is outputted but can't get past the user line. In other languages I would simply mock UserManager.FindById to always return some predefined object, but I can't get it to work.
Have been trying to follow the approach given here example mocking the IUserStore but can't get it to work with my example. To mock FindByNameAsync they have used store.As
Any advice would be gratefully received.
My attempt was to follow a similar method to that in link above. Obviously IUserPasswordStore is the wrong interface, but I am not sure how to find the correct one.
var store = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
store.As<IUserPasswordStore<ApplicationUser>>()
.Setup(x => x.FindById(It.IsAny<string>()))
.Returns(ApplicationUser)null);
EntitiesController controller = new EntitiesController();
var result = controller.Index() as ViewResult;
Assert.AreEqual("Index", result.ViewName);
So, thanks to the guidance from #Nkosi and #haim770 I think I have an answer. It still seems overly complex though, so would be interested to hear if you know how to simplify.
It started with needing to write more testable code, I installed Unity and started to inject dependencies allowing me to mock them out.
The solution that passes is:
Mock<IPrincipal> mockPrincipal;
string username = "test#test.com";
[TestInitialize]
public void TestInitialize()
{
//Arrange
var identity = new GenericIdentity(username, "");
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
}
[TestMethod]
public void EntitiesIndexDisplaysTheDefaultView()
{
var context = new Mock<HttpContextBase>();
var principal = mockPrincipal.Object;
context.Setup(x => x.User).Returns(principal);
var userManagerMock = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
userManagerMock.As<IUserPasswordStore<ApplicationUser>>()
.Setup(x => x.FindByIdAsync(It.IsAny<string>()))
.ReturnsAsync(new ApplicationUser() { entityId = "id" });
var entitiesRepositoryMock = new Mock<IEntityRepository>();
entitiesRepositoryMock
.Setup(x => x.GetEntities(It.IsAny<string>()))
.Returns((IEnumerable<Entity>)new List<Entity>());
EntitiesController controller = new EntitiesController(new UserManager<ApplicationUser>(userManagerMock.Object),
entitiesRepositoryMock.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("", result.ViewName);
}

Hooking Up Unit Tests to Local IIS Web Instance

I'm trying to run unit tests for a project which is reliant on the web instance as it uses integrated windows authentication to verify the current user. When I run the tests it doesn't fire up the web instance, which is expected, but I need it to in order for any tests to succeed really. Anyone have any idea of how to do this?
Okay, I got this working in the end. You have to mock HttpContextBase (which is preferential over HttpContext I hear) and then set up your IPrincipal which is returned from the mock object. I'll give the full code listing so you get the idea:
[TestInitialize]
public void SetUp()
{
userRepositoryMock = new Mock<IUserRepository>();
controller = new AccountController(userRepositoryMock.Object);
httpContextMock = new Mock<HttpContextBase>();
IPrincipal fakeUser = new GenericPrincipal(new GenericIdentity("johnsmith#johns4232mith.com", "Forms"), null);
httpContextMock.Setup(x => x.User).Returns(fakeUser);
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>();
var identity = new Mock<IIdentity>();
request.Setup(req => req.ApplicationPath).Returns("~/");
request.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/");
request.Setup(req => req.PathInfo).Returns(string.Empty);
response.Setup(res => res.ApplyAppPathModifier(It.IsAny<string>()))
.Returns((string virtualPath) => virtualPath);
identity.SetupGet(ident => ident.IsAuthenticated).Returns(true);
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);
context.Setup(ctx => ctx.User).Returns(fakeUser);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
}

Problem with TempData and Faked HttpContext using ASP.NET MVC

I am working with a faked HttpContext (code provided in the end) and probably I am missing something because I can't access TempData collection (forth line of SetFakeControllerContext method). Every time I try I get this error message:
'controller.TempData' threw an exception of type 'System.AccessViolationException'
The code that calls FakeHttpContext is:
public static void SetFakeControllerContext(this Controller controller)
{
HttpContextBase httpContext = FakeHttpContext(string.Empty);
var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;
controller.TempData = new TempDataDictionary(); //This is not necessary! It was just a test!!!!
}
Does anybody know what I am doing wrong?
public static HttpContextBase FakeHttpContext(string username){
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>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
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);
context.Setup(ctx => ctx.User).Returns(user.Object);
user.Setup(ctx => ctx.Identity).Returns(identity.Object);
if (!string.IsNullOrEmpty(username))
{
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns(username);
}
else
{
identity.Setup(id => id.IsAuthenticated).Returns(false);
identity.Setup(id => id.Name).Returns(string.Empty);
}
context.Setup(ctx => ctx.Response.Cache).Returns(CreateCachePolicy());
return context.Object;
}
P.s.: I am using Moq
UPDATE:
OMG!! I CAN'T BELIEVE IT! More than 2 hours to figure out that the problem was a reference to the wrong MVC dll. I was referencing System.Web.Mvc 2.0 for my main app but System.Web.Mvc 1.0 in another project. Sorry about this!
The problem's definitely somewhere else. You don't even need to initialize the TempData property to a new dictionary in your unit test. The following program works fine:
public class HomeController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return View();
}
}
class Program
{
static void Main()
{
var controller = new HomeController();
controller.Index();
Console.WriteLine(controller.TempData["foo"]);
}
}

How do you get unit tests to use routes in ASP.NET MVC?

I am writing unit tests against my ASP.NET MVC application, in particular I am testing an HtmlHelper extension method that I wrote. There is a line inside of the extension method:
var innerHtml = htmlHelper.ActionLink(text, action, controller, routeValues, null);
When I run this inside of my unit test, the href of the generated URL is blank regardless of the action or controller passed in.
Here is my unit test:
var page = CreateProductDataPage(); //returns ProductDataPage object
var htmlHelper = Http.CreateHtmlHelperWithMocks<ProductDataPage>(new ViewDataDictionary<ProductDataPage>(page), false);
var result = htmlHelper.ProductListingBreadcrumb(true, null, null);
Here is the CreateHtmlHelperWithMocks method:
public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
{
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);
return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object);
}
Finally, here is the GetViewContextMock method:
public static Mock<ViewContext> GetViewContextMock(ViewDataDictionary viewData, bool isLoggedIn)
{
var mock = new Mock<ViewContext>();
mock.SetupGet(v => v.HttpContext).Returns(GetHttpContextMock(isLoggedIn).Object);
mock.SetupGet(v => v.Controller).Returns(new Mock<ControllerBase>().Object);
mock.SetupGet(v => v.View).Returns(new Mock<IView>().Object);
mock.SetupGet(v => v.ViewData).Returns(viewData);
mock.SetupGet(v => v.TempData).Returns(new TempDataDictionary());
mock.SetupGet(v => v.RouteData).Returns(new RouteData());
return mock;
}
Update: Figured it out. What a pain in the a$$. In case anyone else tries to do this...
The first step was to add the Route Collection from the global.asax in the creation of the mock HtmlHelper.
public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
{
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);
//These next two lines are key:
var routeCollection = new RouteCollection();
MvcApplication.RegisterRoutes(routeCollection);
return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object, routeCollection);
}
Then I had to make sure the HttpContext mock had a result being returned for the Request's ApplicationPath property and the Response's ApplyAppPathModifier method.
public static Mock<HttpContextBase> GetHttpContextMock(bool isLoggedIn)
{
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>();
var principal = AuthenticationAndAuthorization.GetPrincipleMock(isLoggedIn);
//These next two lines are required for the routing to generate valid URLs, apparently:
request.SetupGet(r => r.ApplicationPath).Returns("/");
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((string r) => r);
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
context.SetupGet(c => c.User).Returns(principal.Object);
return context;
}
I blogged about doing this with Rhino.Mocks about a month ago. You can find more information on how I handle this at http://farm-fresh-code.blogspot.com/2009/10/mocking-htmlhelper-class-with.html. Basically, my solution is to provide everything in the mock, both in the RouteData and via the ApplyAppPathModifier on the Response object connected to the mock helper. It's actually more of a fake helper based on underlying stubs.

ASP.NET MVC: Unit testing controllers that use UrlHelper

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 a lot 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.
Here is one of my tests (xUnit + Moq) just for similar case (using Url.RouteUrl in controller)
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);
A modified implementation from eu-ge-ne. This one returns a generated link based on the routes defined in the application. eu-ge-ne's example always returned a fixed response. The approach below will allow you to test that the correct action/controller and route information is being passed into the UrlHelper - which is what you want if you are testing call to the UrlHelper.
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>();
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);
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 NameValueCollection());
response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
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);

Resources