Moq: Unit Testing a Method Relying on Httpcontext

Moq: unit testing a method relying on HttpContext

Webforms is notoriously untestable for this exact reason - a lot of code can rely on static classes in the asp.net pipeline.

In order to test this with Moq, you need to refactor your GetSecurityContextUserName() method to use dependency injection with an HttpContextBase object.

HttpContextWrapper resides in System.Web.Abstractions, which ships with .Net 3.5. It is a wrapper for the HttpContext class, and extends HttpContextBase, and you can construct an HttpContextWrapper just like this:

var wrapper = new HttpContextWrapper(HttpContext.Current);

Even better, you can mock an HttpContextBase and set up your expectations on it using Moq. Including the logged in user, etc.

var mockContext = new Mock<HttpContextBase>();

With this in place, you can call GetSecurityContextUserName(mockContext.Object), and your application is much less coupled to the static WebForms HttpContext. If you're going to be doing a lot of tests that rely on a mocked context, I highly suggest taking a look at Scott Hanselman's MvcMockHelpers class, which has a version for use with Moq. It conveniently handles a lot of the setup necessary. And despite the name, you don't need to be doing it with MVC - I use it successfully with webforms apps when I can refactor them to use HttpContextBase.

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

How to mock HttpContext.User

Make a mock http context

private class MockHttpContext : HttpContextBase {
private readonly IPrincipal user;

public MockHttpContext(IPrincipal principal) {
this.user = principal;
}

public override IPrincipal User {
get {
return user;
}
set {
base.User = value;
}
}
}

Arrange test accordingly.

[Test]
public void ViewDocuments_WhenCalled_ShouldReturnViewModel() {
// Arrange
var principal = new CustomPrincipal("2038786");
principal.UserId = "2038786";
principal.FirstName = "Test";
principal.LastName = "User";
principal.IsStoreUser = true;

var mockUoW = new Mock<IUnitOfWork>();
//...setup UoW dependency if needed
var controller = new DocumentsController(mockUoW.Object);
controller.ControllerContext = new ControllerContext {
Controller = controller,
HttpContext = new MockHttpContext(principal)
};

// Act
var result = controller.ViewDocuments();

//Assert
//...assertions
}

Don't mock system under test. Mock its dependencies.

How can I make HttpContext available to be used by my Unit Tests?

You shouldn't use HttpContext.Current directly in your function as it is close to impossible to unit test, as you've already found out. I would suggest you using HttpContextBase instead, which is passed either in the constructor of your class or as an argument to the method you are testing. This will allow the consumers of this class to pass a real HttpContextWrapper and in your unit test you can mock the methods you need.

For example here's how you could call the method:

var wrapper = new HttpContextWrapper(HttpContext.Current);
Foo.UploadedFile(wrapper);

And in your unit test (using Rhino Mocks):

var contextMock = MockRepository.GenerateMock<HttpContextBase>();
// TODO: Define expectations on the mocked object
Foo.UploadedFile(contextMock);

Or, if you prefer, use Constructor Injection.

OpenRasta Mocking HttpContext in Unit Tests

You should not need to access HttpContext.Current directly. You should be able to use one of the available dependencies which are easily mockable.

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.



Related Topics



Leave a reply



Submit