I am working with a faked HttpContext (code provided in the end) and probably I am missing something because I can't access TempData collection (forth line of SetFakeControllerContext method). Every time I try I get this error message:
'controller.TempData' threw an exception of type 'System.AccessViolationException'
The code that calls FakeHttpContext is:
public static void SetFakeControllerContext(this Controller controller)
{
HttpContextBase httpContext = FakeHttpContext(string.Empty);
var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;
controller.TempData = new TempDataDictionary(); //This is not necessary! It was just a test!!!!
}
Does anybody know what I am doing wrong?
public static HttpContextBase FakeHttpContext(string username){
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(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);
if (!string.IsNullOrEmpty(username))
{
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns(username);
}
else
{
identity.Setup(id => id.IsAuthenticated).Returns(false);
identity.Setup(id => id.Name).Returns(string.Empty);
}
context.Setup(ctx => ctx.Response.Cache).Returns(CreateCachePolicy());
return context.Object;
}
P.s.: I am using Moq
UPDATE:
OMG!! I CAN'T BELIEVE IT! More than 2 hours to figure out that the problem was a reference to the wrong MVC dll. I was referencing System.Web.Mvc 2.0 for my main app but System.Web.Mvc 1.0 in another project. Sorry about this!
The problem's definitely somewhere else. You don't even need to initialize the TempData property to a new dictionary in your unit test. The following program works fine:
public class HomeController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return View();
}
}
class Program
{
static void Main()
{
var controller = new HomeController();
controller.Index();
Console.WriteLine(controller.TempData["foo"]);
}
}
Related
I have this function and unit test.
ProfileController Code
[HttpPost]
public ActionResult Edit(int? id)
{
var redirectUrl = new UrlHelper(Request.RequestContext).Action("Index", "Profile", new { id = 0 });
return Json(new { Url = redirectUrl });
}
unit test code
[TestMethod]
public void TestDetailsViewData()
{
var controller = new ProfileController(_Profile);
var result = controller.Edit(1) as ViewResult;
var profile = (VIEWMODELS.Customer.Profile)result.ViewData.Model;
Assert.AreEqual("Testor", profile.cardName);
}
i would like to test this function and this function will redirect to index page and return ViewPage with data. But the problem is when i run this unit test code i got Null exception at this line ,because of the Request is NULL
var redirectUrl = new UrlHelper(Request.RequestContext).Action("Index", "Profile", new { id = 0 });
so may i know how could i test this?
Updated with Moq function
After further reading and get some sample code from here it seem request able to filled with value but now the RequestContext is NULL, anyone can point me to right place for me to study?
[TestMethod]
public void TestDetailsViewData()
{
var request = new Mock<HttpRequestBase>();
request.SetupGet(x => x.Headers).Returns(
new System.Net.WebHeaderCollection {
{"X-Requested-With", "XMLHttpRequest"}
});
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
//context.SetupGet(x => x.Request.RequestContext ).Returns(request.Object.RequestContext);
var controller = new ProfileController(_Profile);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Request.RequestContext = context.Object.Request.RequestContext;
var result = controller.Edit(1) as ViewResult;
var profile = (VIEWMODELS.Customer.Profile)result.ViewData.Model;
Assert.AreEqual("testor", profile.cardName);
}
The error is occurring because the request is not defined in Test method.I think you have to use visual studio fakes.
https://msdn.microsoft.com/en-us/library/hh549175.aspx
I have the following unit test defined to test my model binder:
[TestMethod]
public void DateTime_Works() {
// Arrange
var controllerContext = FakeContext.GetAuthenticatedControllerContext(new Mock<Controller>().Object, "TestAdmin");
var values = new NameValueCollection {
{ "Foo.Date", "12/02/1964" },
{ "Foo.Hour", "12" },
{ "Foo.Minute", "00" },
{ "Foo.Second", "00" }
};
var bindingContext = new ModelBindingContext() { ModelName = "Foo", ValueProvider = new NameValueCollectionValueProvider(values, null) };
var binder = new DateAndTimeModelBinder();
// Act
var result = (DateTime)binder.BindModel(controllerContext, bindingContext);
// Assert
Assert.AreEqual(DateTime.Parse("1964-12-02 12:00:00"), result);
}
Here is the FakeContext class:
public static class FakeContext {
public static HttpContextBase GetHttpContext(string username) {
var context = new Mock<HttpContextBase>();
context.SetupGet(ctx => ctx.Request.IsAuthenticated).Returns(!string.IsNullOrEmpty(username));
return context.Object;
}
public static ControllerContext GetControllerContext(Controller controller) {
return GetAuthenticatedControllerContext(controller, null);
}
public static ControllerContext GetAuthenticatedControllerContext(Controller controller, string username) {
var httpContext = GetHttpContext(username);
return new ControllerContext(httpContext, new RouteData(), controller);
}
}
Within my model binder i call a utility method which has the following line in it:
HttpContext.Current.User.Identity.IsAuthenticated
This seems to always return false even when I pass a username in. I was wondering how the GetHttpContext method can be modified to mock this out aswell.
I'd appreciate the help. Thanks
You are mocking the IsAuthenticated property of the Request and not of the User Identity. Your mock is indicating that the request has been authenticated, but has nothing to do with the identity authentication.
Changing your mock up a little should fix your problem.
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet( i => i.IsAuthenticated ).Returns( !string.IsNullOrEmpty(username));
var userMock = new Mock<IPrincipal>();
userMock.SetupGet( u => u.Identity ). Returns( identityMock.Object );
var context = new Mock<HttpContextBase>();
context.SetupGet( ctx => ctx.User ).Returns( userMock.Object );
Edit The more I thought about it the more I felt these mocks needed to be setup seperatly
Note, I have not executed this code. It should get you what you need however.
MSDN for Request.IsAuthenticated
http://msdn.microsoft.com/en-us/library/system.web.httprequest.isauthenticated.aspx
MSDN for User.Identity.IsAuthenticated
http://msdn.microsoft.com/en-us/library/system.security.principal.iidentity.isauthenticated.aspx
Hope this helps.
You could use Moq and follow the advice found here:
Moq: unit testing a method relying on HttpContext
I'm using this helper method to turn my PartialViewResult into string and returning it as Json - http://www.atlanticbt.com/blog/asp-net-mvc-using-ajax-json-and-partialviews/
My problem is that I'm using Moq to mock the controller, and whenever I run unit test that uses this RenderPartialViewToString() method, I got the "Object reference not set to an instance of an object." error on ControllerContext.
private ProgramsController GetController()
{
var mockHttpContext = new Mock<ControllerContext>();
mockHttpContext.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("test");
mockHttpContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
// Mock Repositories
var mockOrganizationRepository = new MockOrganizationRepository(MockData.MockOrganizationsData());
var mockIRenderPartial = new BaseController();
var controller = new ProgramsController(mockOrganizationRepository, mockIRenderPartial);
controller.ControllerContext = mockHttpContext.Object;
return controller;
}
This returns a proxy controller, and maybe it's the reason why I got that error. Any idea how to unit testing this?
Thank you very much.
try this:
public static void SetContext(this Controller controller)
{
var httpContextBase = new Mock<HttpContextBase>();
var httpRequestBase = new Mock<HttpRequestBase>();
var respone = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var routes = new RouteCollection();
RouteConfigurator.RegisterRoutesTo(routes);
httpContextBase.Setup(x => x.Response).Returns(respone.Object);
httpContextBase.Setup(x => x.Request).Returns(httpRequestBase.Object);
httpContextBase.Setup(x => x.Session).Returns(session.Object);
session.Setup(x => x["somesessionkey"]).Returns("value");
httpRequestBase.Setup(x => x.Form).Returns(new NameValueCollection());
controller.ControllerContext = new ControllerContext(httpContextBase.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(controller.HttpContext, new RouteData()), routes);
}
I am writing unit tests against my ASP.NET MVC application, in particular I am testing an HtmlHelper extension method that I wrote. There is a line inside of the extension method:
var innerHtml = htmlHelper.ActionLink(text, action, controller, routeValues, null);
When I run this inside of my unit test, the href of the generated URL is blank regardless of the action or controller passed in.
Here is my unit test:
var page = CreateProductDataPage(); //returns ProductDataPage object
var htmlHelper = Http.CreateHtmlHelperWithMocks<ProductDataPage>(new ViewDataDictionary<ProductDataPage>(page), false);
var result = htmlHelper.ProductListingBreadcrumb(true, null, null);
Here is the CreateHtmlHelperWithMocks method:
public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
{
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);
return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object);
}
Finally, here is the GetViewContextMock method:
public static Mock<ViewContext> GetViewContextMock(ViewDataDictionary viewData, bool isLoggedIn)
{
var mock = new Mock<ViewContext>();
mock.SetupGet(v => v.HttpContext).Returns(GetHttpContextMock(isLoggedIn).Object);
mock.SetupGet(v => v.Controller).Returns(new Mock<ControllerBase>().Object);
mock.SetupGet(v => v.View).Returns(new Mock<IView>().Object);
mock.SetupGet(v => v.ViewData).Returns(viewData);
mock.SetupGet(v => v.TempData).Returns(new TempDataDictionary());
mock.SetupGet(v => v.RouteData).Returns(new RouteData());
return mock;
}
Update: Figured it out. What a pain in the a$$. In case anyone else tries to do this...
The first step was to add the Route Collection from the global.asax in the creation of the mock HtmlHelper.
public static HtmlHelper<T> CreateHtmlHelperWithMocks<T>(ViewDataDictionary<T> viewData, bool isLoggedIn) where T : class
{
var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.SetupGet(v => v.ViewData).Returns(viewData);
//These next two lines are key:
var routeCollection = new RouteCollection();
MvcApplication.RegisterRoutes(routeCollection);
return new HtmlHelper<T>(GetViewContextMock(viewData, isLoggedIn).Object, mockViewDataContainer.Object, routeCollection);
}
Then I had to make sure the HttpContext mock had a result being returned for the Request's ApplicationPath property and the Response's ApplyAppPathModifier method.
public static Mock<HttpContextBase> GetHttpContextMock(bool isLoggedIn)
{
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 principal = AuthenticationAndAuthorization.GetPrincipleMock(isLoggedIn);
//These next two lines are required for the routing to generate valid URLs, apparently:
request.SetupGet(r => r.ApplicationPath).Returns("/");
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((string r) => r);
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
context.SetupGet(c => c.User).Returns(principal.Object);
return context;
}
I blogged about doing this with Rhino.Mocks about a month ago. You can find more information on how I handle this at http://farm-fresh-code.blogspot.com/2009/10/mocking-htmlhelper-class-with.html. Basically, my solution is to provide everything in the mock, both in the RouteData and via the ApplyAppPathModifier on the Response object connected to the mock helper. It's actually more of a fake helper based on underlying stubs.
I have a controller in C# using the ASP.Net MVC framework
public class HomeController:Controller{
public ActionResult Index()
{
if (Request.IsAjaxRequest())
{
//do some ajaxy stuff
}
return View("Index");
}
}
I got some tips on mocking and was hoping to test the code with the following and RhinoMocks
var mocks = new MockRepository();
var mockedhttpContext = mocks.DynamicMock<HttpContextBase>();
var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>();
SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest);
var controller = new HomeController();
controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("About", result.ViewName);
However I keep getting this error:
Exception
System.ArgumentNullException:
System.ArgumentNullException : Value
cannot be null. Parameter name:
request at
System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest(HttpRequestBase
request)
Since the Request object on the controller has no setter. I tried to get this test working properly by using recommended code from an answer below.
This used Moq instead of RhinoMocks, and in using Moq I use the following for the same test:
var request = new Mock<HttpRequestBase>();
// Not working - IsAjaxRequest() is static extension method and cannot be mocked
// request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */);
// use this
request.SetupGet(x => x.Headers["X-Requested-With"]).Returns("XMLHttpRequest");
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
var controller = new HomeController(Repository, LoginInfoProvider);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("About", result.ViewName);
but get the following error:
Exception System.ArgumentException:
System.ArgumentException : Invalid
setup on a non-overridable member: x
=> x.Headers["X-Requested-With"] at Moq.Mock.ThrowIfCantOverride(Expression
setup, MethodInfo methodInfo)
Again, it seems like I cannot set the request header.
How do I set this value, in RhinoMocks or Moq?
Using Moq:
var request = new Mock<HttpRequestBase>();
// Not working - IsAjaxRequest() is static extension method and cannot be mocked
// request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */);
// use this
request.SetupGet(x => x.Headers).Returns(
new System.Net.WebHeaderCollection {
{"X-Requested-With", "XMLHttpRequest"}
});
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
var controller = new YourController();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
UPDATED:
Mock Request.Headers["X-Requested-With"] or Request["X-Requested-With"] instead of Request.IsAjaxRequest().
For anyone using NSubstitute I was able to modify the above answers and do something like this... (where Details is the Action method name on the controller)
var fakeRequest = Substitute.For<HttpRequestBase>();
var fakeContext = Substitute.For<HttpContextBase>();
fakeRequest.Headers.Returns(new WebHeaderCollection { {"X-Requested-With", "XMLHttpRequest"}});
fakeContext.Request.Returns(fakeRequest);
controller.ControllerContext = new ControllerContext(fakeContext, new RouteData(), controller);
var model = new EntityTypeMaintenanceModel();
var result = controller.Details(model) as PartialViewResult;
Assert.IsNotNull(result);
Assert.AreEqual("EntityType", result.ViewName);
Here is a working solution using RhinoMocks. I've based it on a Moq solution I found at http://thegrayzone.co.uk/blog/2010/03/mocking-request-isajaxrequest/
public static void MakeAjaxRequest(this Controller controller)
{
MockRepository mocks = new MockRepository();
// Create mocks
var mockedhttpContext = mocks.DynamicMock<HttpContextBase>();
var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>();
// Set headers to pretend it's an Ajax request
SetupResult.For(mockedHttpRequest.Headers)
.Return(new WebHeaderCollection() {
{"X-Requested-With", "XMLHttpRequest"}
});
// Tell the mocked context to return the mocked request
SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest);
mocks.ReplayAll();
// Set controllerContext
controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller);
}
Is AjaxRequest is an extension method. So you can do it the following way using Rhino:
protected HttpContextBase BuildHttpContextStub(bool isAjaxRequest)
{
var httpRequestBase = MockRepository.GenerateStub<HttpRequestBase>();
if (isAjaxRequest)
{
httpRequestBase.Stub(r => r["X-Requested-With"]).Return("XMLHttpRequest");
}
var httpContextBase = MockRepository.GenerateStub<HttpContextBase>();
httpContextBase.Stub(c => c.Request).Return(httpRequestBase);
return httpContextBase;
}
// Build controller
....
controller.ControllerContext = new ControllerContext(BuildHttpContextStub(true), new RouteData(), controller);
Looks like you are looking for this,
var requestMock = new Mock<HttpRequestBase>();
requestMock.SetupGet(rq => rq["Age"]).Returns("2001");
Usage in Controller :
public ActionResult Index()
{
var age = Request["Age"]; //This will return 2001
}
In current .NET (v 5):
var controller = new SomeController(); // SomeController that inherits Microsoft.AspNetCore.Mvc.ControllerBase
var httpContext = new DefaultHttpContext(); // DefaultHttpContext class is part of Microsoft.AspNetCore.Http namespace
httpContext.Request.Headers.Add("origin", "0.0.0.1"); // Add your custom headers to request
controller.ControllerContext.HttpContext = httpContext;
You need to mock HttpContextBase and put it into your ControllerContext property, like that:
controller.ControllerContext =
new ControllerContext(mockedHttpContext, new RouteData(), controller);
To make IsAjaxRequest() to return false during Unit test you need to setup Request Headers as well as request collection value both in your test method as given below:
_request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "NotAjaxRequest" } });
_request.SetupGet(x=>x["X-Requested-With"]).Returns("NotAjaxRequest");
The reason for setting up both is hidden in implementation of IsAjaxRequest() which is given below:
public static bool IsAjaxRequest(this HttpRequestBase request)<br/>
{
if (request == null)
{
throw new ArgumentNullException("request");
}
return ((request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest")));
}
It uses both request Collection and header this is why we need to create setup for both Header and Request Collection.
this will make the request to return false when it is not a ajax request. to make it return true you can do the following:
_httpContext.SetupGet(x => x.Request["X-Requested-With"]).Returns("XMLHttpRequest");
I found other way to add a HttpRequestMessage object into your request during Web API as follow
[Test]
public void TestMethod()
{
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("TestHeader", "TestHeader");
controllerContext.Request = request;
_controller.ControllerContext = controllerContext;
var result = _controller.YourAPIMethod();
//Your assertion
}
(A bit late to the party but I went for a different route so thought I'd share)
To go for a pure code/mocking way of testing this without creating mocks for Http classes I implemented an IControllerHelper which has an Initialise method which takes the Request as a parameter and then exposed properties I want eg:
public interface IControllerHelper
{
void Initialise(HttpRequest request);
string HostAddress { get; }
}
public class ControllerHelper : IControllerHelper
{
private HttpRequest _request;
public void Initialise(HttpRequest request)
{
_request = request;
}
public string HostAddress => _request.GetUri().GetLeftPart(UriPartial.Authority);
}
Then in my controller I call initialise at the start of the method:
_controllerHelper.Initialise(Request);
And then my code is only dependent on mockable dependacies.
return Created(new Uri($"{_controllerHelper.HostName}/api/MyEndpoint/{result.id}"), result);
For functional tests I just override the iControllerHelper in the composition for a substitute.