How to Mock the Httpcontext in ASP.NET MVC Using Moq

Mock HttpContext in web api using MOQ

How do we feel about this?

public interface IHttpContext
{
string Url { get; }
}

public class HttpContextProvider : IHttpContext
{
public string Url => HttpContext.Current.Request.Url.ToString();
}

public class MockedHttpContextProvider : IHttpContext
{
public string Url => "https://google.com";
}

I agree you shouldn't pass HttpContext deep into your application, but if you are refactoring old code and it's already been passed a long way down the tree, I find the below is a great way to write unit tests for your new code and not have to touch other peoples stuff!

Mock HttpContext using moq for unit test

This is a common question here and the root of the problem is DON'T MOCK TYPES YOU DON'T OWN. Rather than complicated mocks to try and reproduce the behavior of a class you didn't write (which means you're guessing about what it does and what you need to mock), introduce an abstraction between your code and the external object HttpContext and mock that abstraction. As one of the pioneers of mock objects says:

The key for me was when Joe Walnes came up with the radical notion of
"Don't mock types you don't own". This means: stop jumping through
hoops to work with closed-world libraries, use the tests to discover
your objects and what they say to each other.

http://higherorderlogic.com/2004/02/the-big-idea-is-messaging/

Mocking HttpContextBase with Moq

I'm using a version of some code Steve Sanderson included in his Pro Asp.NET MVC book... and I'm currently having a moral dilemma whether it's okay to post the code here. How about I compromise with a highly stripped down version? ;)

So this can easily be reused, create a class similar to the one below that you will pass your controller. This will set up your mocks and set them to your controller's ControllerContext

public class ContextMocks
{
public Moq.Mock<HttpContextBase> HttpContext { get; set; }
public Moq.Mock<HttpRequestBase> Request { get; set; }
public RouteData RouteData { get; set; }

public ContextMocks(Controller controller)
{
//define context objects
HttpContext = new Moq.Mock<HttpContextBase>();
HttpContext.Setup(x => x.Request).Returns(Request.Object);
//you would setup Response, Session, etc similarly with either mocks or fakes

//apply context to controller
RequestContext rc = new RequestContext(HttpContext.Object, new RouteData());
controller.ControllerContext = new ControllerContext(rc, controller);
}
}

And then in your test method you'd just create an instance of ContextMocks and pass in the controller object you're testing:

[Test]
Public void test()
{
var mocks = new ContextMocks(controller);
var req = controller.Request;
//do some asserts on Request object
}

Seems very similar to Craig's examples, but this is with Moq v3. I have to give props to Steve Sanderson for this - I'm using this as a basis for testing all kinds of otherwise traditionally hard-to-test stuff: cookies, session, request method, querystring and more!

How to moq HttpContext on Asp net Core

You can use ControllerContext to set the context to be DefaultHttpContext which you can modify to your needs.

var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext()};
var tested = new MyCtrl();
tested.ControllerContext = ctx;

Unit testing controller using MOQ . How to mock httpcontext

In this particular issue you can simply overwrite controller's Url property with mocked UrlHelper class.

For HttpContext mocking, it might be good to inject HttpContextBase to your controller and configure your DI container to serve the proper one for you. It would ease mocking it later for testing purposes. I believe Autofac has some automatic way to configure container for ASP.NET-related classes like HttpContextBase.

EDIT

It seems you can't mock UrlHelper with Moq, as @lazyberezovsky wrote - you can mock only interfaces and virtual methods. But it does not stop you from writing your own mocked object. That's true you need to mock HttpContext, as it's required by UrlHelper constructor (actually, it's required by RequestContext constructor, which is required by UrlHelper constructor)... Moreover, IsLocalUrl does not use anything from context, so you do not have to provide any additional setup.

Sample code would look like that:

Controller:

public ActionResult Foo(string url)
{
if (Url.IsLocalUrl(url))
{
return Redirect(url);
}

return RedirectToAction("Index", "Home");
}

Tests:

[TestClass]
public class HomeControllerTests
{
private Mock<HttpContextBase> _contextMock;
private UrlHelper _urlHelperMock;

public HomeControllerTests()
{
_contextMock = new Mock<HttpContextBase>();

_urlHelperMock = new UrlHelper(new RequestContext(_contextMock.Object, new RouteData()));
}


[TestMethod]
public void LocalUrlTest()
{
HomeController controller = new HomeController();
controller.Url = _urlHelperMock;

RedirectResult result = (RedirectResult)controller.Foo("/");
Assert.AreEqual("/", result.Url);
}

[TestMethod]
public void ExternalUrlTest()
{
HomeController controller = new HomeController();
controller.Url = _urlHelperMock;

RedirectToRouteResult result = (RedirectToRouteResult)controller.Foo("http://test.com/SomeUrl");
Assert.AreEqual("Index", result.RouteValues["action"]);
Assert.AreEqual("Home", result.RouteValues["controller"]);
}
}

Mock HttpContext for unit testing a .NET core MVC controller?

I was able to initialize the httpcontext and header in this way:

[TestMethod]
public void TestValuesController()
{
ValuesController controller = new ValuesController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.ControllerContext.HttpContext.Request.Headers["device-id"] = "20317";
var result = controller.Get();
//the controller correctly receives the http header key value pair device-id:20317
...
}

Mocking HttpContext.Current using moq on NUnit Test

In the long run, you'll be happier if you create an interface for the SessionVar class. Use your current implementation (via Dependency Injection) at runtime. Plug in a mock during testing. Removes the need for mocking out all of those Http runtime dependencies.



Related Topics



Leave a reply



Submit