Mock HttpContext.Current in Test Init Method
HttpContext.Current
returns an instance of System.Web.HttpContext
, which does not extend System.Web.HttpContextBase
. HttpContextBase
was added later to address HttpContext
being difficult to mock. The two classes are basically unrelated (HttpContextWrapper
is used as an adapter between them).
Fortunately, HttpContext
itself is fakeable just enough for you do replace the IPrincipal
(User) and IIdentity
.
The following code runs as expected, even in a console application:
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]
);
How do I unit test HttpContext.Current.Request.LogonUserIdentity.Name ?
I spent a lot of time trying to mock HttpContext or HttpContextBase, then trying to shim with fakes WindowsIdentity class or HttpRequest.LogonUserIdentity property.
Nothing works - you don't need completely mocked HttpContext because you want see real responses, not your mock set ups returned.
Shims are just not generating for WindowsIdentity class ("due to internal limitations") and for LogonUserIdentity property (no reason given in fakes messages, it's just not there).
The best approach how to get testable HttpContext with request and response is described here:
http://jonlanceley.blogspot.com/2015/08/unit-testing-part-2-faking-httpcontext.html
I was able to override LogonUserIdentity property in my fake request wrapper and set there whatever I need.
How to create unit test with HttpContext for authorization inside a method?
HttpContext.Current
is static and can be assigned:
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]
);
Code from Mock HttpContext.Current in Test Init Method
Setting HttpContext.Current.Application key in unit tests
What I ended up doing is using this amazing library. For those who wonder why nothing worked, this StackOverflow question explains a similar problem (strange how I couldn't find it before asking).
According to Jeff Sternal's answer:
HttpContext.Application is supplied by a private singleton that you can't access normally.
You may be able to set this via reflection, but I personally wouldn't even bother.
Instead you should isolate your testable code from global state like HttpContext.Current: just pass the value you're looking for into the method you want to test.
You can see the problem clearly looking at the code in Reflector (or in the .NET source if you've downloaded that): the HttpContext.Application get accessor returns a new HttpApplicationState instance every time you call it unless something (like the ASP.NET framework) sets HttpApplicationFactory._theApplicationFactory._state.
Eventually, I ended up doing something like:
using (HttpSimulator simulator = new HttpSimulator())
{
simulator.SimulateRequest(new Uri("http://localhost/"));
HttpContext.Current.Application.Add("fooKey", Foo);
this.httpContext = HttpContext.Current;
}
And then passed the saved reference to my method.
Setting HttpContext.Current.Session in a unit test
We had to mock HttpContext
by using a HttpContextManager
and calling the factory from within our application as well as the Unit Tests
public class HttpContextManager
{
private static HttpContextBase m_context;
public static HttpContextBase Current
{
get
{
if (m_context != null)
return m_context;
if (HttpContext.Current == null)
throw new InvalidOperationException("HttpContext not available");
return new HttpContextWrapper(HttpContext.Current);
}
}
public static void SetCurrentContext(HttpContextBase context)
{
m_context = context;
}
}
You would then replace any calls to HttpContext.Current
with HttpContextManager.Current
and have access to the same methods. Then when you're testing, you can also access the HttpContextManager
and mock your expectations
This is an example using Moq:
private HttpContextBase GetMockedHttpContext()
{
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>();
var urlHelper = new Mock<UrlHelper>();
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var requestContext = new Mock<RequestContext>();
requestContext.Setup(x => x.HttpContext).Returns(context.Object);
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);
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns("test");
request.Setup(req => req.Url).Returns(new Uri("http://www.google.com"));
request.Setup(req => req.RequestContext).Returns(requestContext.Object);
requestContext.Setup(x => x.RouteData).Returns(new RouteData());
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
return context.Object;
}
and then to use it within your unit tests, I call this within my Test Init method
HttpContextManager.SetCurrentContext(GetMockedHttpContext());
you can then, in the above method add the expected results from Session that you're expecting to be available to your web service.
How do I unit test a class that depends on HttpContext.Current and Sitecore.Context?
You would need to create a mock HTTPContext and inject it into the method for the test. (You probably need to mock quite a few other objects too, since the method has several dependencies.)
Then, after the method has run, assert that the the response in the context is as you want it.
See details here: Mock HttpContext.Current in Test Init Method
Related Topics
Simple Proof That Guid Is Not Unique
Running Multiple Async Tasks and Waiting For Them All to Complete
Are String.Equals() and == Operator Really Same
How to Dynamically Create Generic C# Object Using Reflection
A Potentially Dangerous Request.Path Value Was Detected from the Client (*)
Json.Net Serialization of Type With Polymorphic Child Object
Calculate the Number of Business Days Between Two Dates
Asp.Net MVC: Custom Validation by Dataannotation
Reflection: How to Invoke Method With Parameters
Getting All Types in a Namespace Via Reflection
Sort a List from Another List Ids