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
How to Add System.Windows.Interactivity to Project
Conversion of a Datetime2 Data Type to a Datetime Data Type Results Out-Of-Range Value
How to Bind a List to a Combobox
How to Deal with Files with a Name Longer Than 259 Characters
How to Embed a Text File in a .Net Assembly
Intersection of Multiple Lists with Ienumerable.Intersect()
Change Wpf Controls from a Non-Main Thread Using Dispatcher.Invoke
How to Pass Command-Line Arguments to a Winforms Application
Why Is It Bad to Use an Iteration Variable in a Lambda Expression
C# Splitting Strings on '#' Character
Where to Learn About VS Debugger 'Magic Names'
How to Convert Seconds into (Hour:Minutes:Seconds:Milliseconds) Time