Problem
I’m attempting to include unit testing into an ASP.NET MVC application that I’ve created. I use the following code in my unit tests:
[TestMethod]
public void IndexAction_Should_Return_View() {
var controller = new MembershipController();
controller.SetFakeControllerContext("TestUser");
...
}
To fake the controller context, use the following helpers:
public static class FakeControllerContext {
public static HttpContextBase FakeHttpContext(string username) {
var context = new Mock<HttpContextBase>();
context.SetupGet(ctx => ctx.Request.IsAuthenticated).Returns(!string.IsNullOrEmpty(username));
if (!string.IsNullOrEmpty(username))
context.SetupGet(ctx => ctx.User.Identity).Returns(FakeIdentity.CreateIdentity(username));
return context.Object;
}
public static void SetFakeControllerContext(this Controller controller, string username = null) {
var httpContext = FakeHttpContext(username);
var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;
}
}
This test class is descended from a base class that includes the following features:
[TestInitialize]
public void Init() {
...
}
This method invokes a library (over which I have no control) that attempts to execute the following code:
HttpContext.Current.User.Identity.IsAuthenticated
You’ve probably figured out what the issue is now. In this base Init method, I did not set the bogus HttpContext against the controller. I’m new to unit testing and mocking, so I want to make sure I’m doing everything correctly. What is the best approach for me to mock out the HttpContext so that it can be shared across my controller and any libraries that my Init function calls?
Asked by nfplee
Solution #1
HttpContext. Current returns a System instance. System is not extended by Web.HttpContext. Web.HttpContextBase. HttpContextBase was added subsequently to solve the difficulty of mocking HttpContext. Both classes are essentially unconnected (HttpContextWrapper is used as an adapter between them).
Fortunately, HttpContext is sufficiently fakeable to allow you to substitute the IPrincipal (User) and IIdentity.
Even in a console application, the following code works as expected:
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://tempuri.org", ""),
new HttpResponse(new StringWriter())
);
// User is logged in
HttpContext.Current.User = new GenericPrincipal(
new GenericIdentity("username"),
new string[0]
);
// User is logged out
HttpContext.Current.User = new GenericPrincipal(
new GenericIdentity(String.Empty),
new string[0]
);
Answered by Richard Szalay
Solution #2
Test Init, which is listed below, can also suffice.
[TestInitialize]
public void TestInit()
{
HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
YourControllerToBeTestedController = GetYourToBeTestedController();
}
Answered by PUG
Solution #3
I realize this is an outdated topic, but mocking an MVC application for unit testing is something we do frequently.
I just wanted to share some of my personal experiences. After upgrading to Visual Studio 2013, mocking an MVC 3 application with Moq 4. When trying to peek at the variables in debug mode, none of the unit tests worked, and the HttpContext displayed “could not evaluate expression.”
Visual Studio 2013 appears to have difficulty analyzing some objects. I have to check the “Use Managed Compatibility Mode” option in Tools=>Options=>Debugging=>General settings to get debugging mocked web apps running again.
This is how I usually proceed:
public static class FakeHttpContext
{
public static void SetFakeContext(this Controller controller)
{
var httpContext = MakeFakeContext();
ControllerContext context =
new ControllerContext(
new RequestContext(httpContext,
new RouteData()), controller);
controller.ControllerContext = context;
}
private static HttpContextBase MakeFakeContext()
{
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(c=> c.Request).Returns(request.Object);
context.Setup(c=> c.Response).Returns(response.Object);
context.Setup(c=> c.Session).Returns(session.Object);
context.Setup(c=> c.Server).Returns(server.Object);
context.Setup(c=> c.User).Returns(user.Object);
user.Setup(c=> c.Identity).Returns(identity.Object);
identity.Setup(i => i.IsAuthenticated).Returns(true);
identity.Setup(i => i.Name).Returns("admin");
return context.Object;
}
}
And by establishing the context in this manner,
FakeHttpContext.SetFakeContext(moController);
And it’s as simple as that to call the Method in the controller.
long lReportStatusID = -1;
var result = moController.CancelReport(lReportStatusID);
Answered by aggaton
Solution #4
If your application uses a third-party redirect, it’s best to fake HttpContext as seen below:
HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("","","","",new StringWriter(CultureInfo.InvariantCulture));
System.Web.HttpContext.Current = new HttpContext(initWorkerRequest);
System.Web.HttpContext.Current.Request.Browser = new HttpBrowserCapabilities();
System.Web.HttpContext.Current.Request.Browser.Capabilities = new Dictionary<string, string> { { "requiresPostRedirectionHandling", "false" } };
Answered by Divang
Post is based on https://stackoverflow.com/questions/4379450/mock-httpcontext-current-in-test-init-method