ASP.NET MVC unit test controller with HttpContext - asp.net-mvc

I am trying to write a unit test for my one controller to verify if a view was returned properly, but this controller has a basecontroller that accesses the HttpContext.Current.Session. Everytime I create a new instance of my controller is calls the basecontroller constructor and the test fails with a null pointer exception on the HttpContext.Current.Session. Here is the code:
public class BaseController : Controller
{
protected BaseController()
{
ViewData["UserID"] = HttpContext.Current.Session["UserID"];
}
}
public class IndexController : BaseController
{
public ActionResult Index()
{
return View("Index.aspx");
}
}
[TestMethod]
public void Retrieve_IndexTest()
{
// Arrange
const string expectedViewName = "Index";
IndexController controller = new IndexController();
// Act
var result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result, "Should have returned a ViewResult");
Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName);
}
Any ideas on how to mock (using Moq) the Session that is accessed in the base controller so the test in the descendant controller will run?

Unless you use Typemock or Moles, you can't.
In ASP.NET MVC you are not supposed to be using HttpContext.Current. Change your base class to use ControllerBase.ControllerContext - it has a HttpContext property that exposes the testable HttpContextBase class.
Here's an example of how you can use Moq to set up a Mock HttpContextBase:
var httpCtxStub = new Mock<HttpContextBase>();
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = httpCtxStub.Object;
sut.ControllerContext = controllerCtx;
// Exercise and verify the sut
where sut represents the System Under Test (SUT), i.e. the Controller you wish to test.

If you are using Typemock, you can do this:
Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"])
.WillReturn("your id");
The test code will look like:
[TestMethod]
public void Retrieve_IndexTest()
{
// Arrange
const string expectedViewName = "Index";
IndexController controller = new IndexController();
Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"])
.WillReturn("your id");
// Act
var result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result, "Should have returned a ViewResult");
Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName);
}

Snippet:
var request = new SimpleWorkerRequest("/dummy", #"c:\inetpub\wwwroot\dummy", "dummy.html", null, new StringWriter());
var context = new HttpContext(request);
SessionStateUtility.AddHttpSessionStateToContext(context, new TestSession());
HttpContext.Current = context;
Implementation of TestSession():
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.SessionState;
namespace m1k4.Framework.Test
{
public class TestSession : IHttpSessionState
{
private Dictionary<string, object> state = new Dictionary<string, object>();
#region IHttpSessionState Members
public void Abandon()
{
throw new NotImplementedException();
}
public void Add(string name, object value)
{
this.state.Add(name, value);
}
public void Clear()
{
throw new NotImplementedException();
}
public int CodePage
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public System.Web.HttpCookieMode CookieMode
{
get
{
throw new NotImplementedException();
}
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public int Count
{
get
{
throw new NotImplementedException();
}
}
public System.Collections.IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
public bool IsCookieless
{
get
{
throw new NotImplementedException();
}
}
public bool IsNewSession
{
get
{
throw new NotImplementedException();
}
}
public bool IsReadOnly
{
get
{
throw new NotImplementedException();
}
}
public bool IsSynchronized
{
get
{
throw new NotImplementedException();
}
}
public System.Collections.Specialized.NameObjectCollectionBase.KeysCollection Keys
{
get
{
throw new NotImplementedException();
}
}
public int LCID
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public SessionStateMode Mode
{
get
{
throw new NotImplementedException();
}
}
public void Remove(string name)
{
this.state.Remove(name);
}
public void RemoveAll()
{
this.state = new Dictionary<string, object>();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public string SessionID
{
get
{
return "Test Session";
}
}
public System.Web.HttpStaticObjectsCollection StaticObjects
{
get
{
throw new NotImplementedException();
}
}
public object SyncRoot
{
get
{
throw new NotImplementedException();
}
}
public int Timeout
{
get
{
return 10;
}
set
{
throw new NotImplementedException();
}
}
public object this[int index]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public object this[string name]
{
get
{
return this.state[name];
}
set
{
this.state[name] = value;
}
}
#endregion
}
}

You should probably use an ActionFilter instead of a base class for this sort of thing
[UserIdBind]
public class IndexController : Controller
{
public ActionResult Index()
{
return View("Index.aspx");
}
}

I'd checkout the ASP.NET-MVC book listed here -- toward the end, there is a good section on Mocking framewors -- http://www.hanselman.com/blog/FreeASPNETMVCEBookNerdDinnercomWalkthrough.aspx

Related

Pass value from ActionFilterAttribute to controller

I have the following base controller with a string variable
public abstract class BaseController:Controller
{
string encryptedSessionGuid;
}
All other controller derives from base controller and ActionMethod has a custom ActionFilterAttribute CheckQueryString-
public class SampleController : BaseController
{
[CheckQueryString(new string[] {"sid"})]
public ActionResult SampleMethod()
{
return View();
}
}
Here is my custom attribute. It sends query string value to view. But I would like to send it base controller variable encryptedSessionGuid also.
public class CheckQueryString : ActionFilterAttribute
{
string[] keys;
public CheckQueryString(string[] Keys) { keys = Keys; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
foreach (var key in keys)
{
if (ctx.Request.QueryString[key] == null)
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
string value = ctx.Request.QueryString[key];
if (string.IsNullOrEmpty(value))
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
var viewBag = filterContext.Controller.ViewData;
viewBag[key] = value;
}
}
}
base.OnActionExecuting(filterContext);
}
}
How can it be done?

MVC IPrincipal User from WebViewPage is null

I've create a base class for my Views like this:
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get
{
if (base.User == null) return null;
return base.User as CustomPrincipal;
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get
{
if (base.User == null) return null;
return base.User as CustomPrincipal;
}
}
public override void Execute()
{
throw new NotImplementedException();
}
}
and in my model I have:
public class SecureAreaModel : BaseViewPage
{
public int MyUserID
{
get { return User.ID; }
private set { }
}
public SecureAreaModel(ControllerContext controllerContext)
{
}
public override void Execute()
{
throw new NotImplementedException();
}
}
I want to use the propertiy MyUserID but I receive this error:
Error
At this point the user is autenticated
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal customer = new CustomPrincipal(serializeModel.Email);
customer.ID = serializeModel.ID;
customer.Email = serializeModel.Email;
customer.FirstName = serializeModel.FirstName;
customer.LastName = serializeModel.LastName;
customer.Roles = serializeModel.Roles;
HttpContext.Current.User = customer;
}
else
{
HttpContext.Current.User = new CustomPrincipal(string.Empty);
}
}
Any help will be appreciated! Thx

Intellisense not working but I have binded table values from SQL DB

I have binded two table values from SQL DB but when I tried to use those table values using Entities it’s not showing in Intellisense. I tried a lot but I failed to get those values in Intellisense. Please help me to fix that. Sorry If I’m using any terms wrong. Please see the below two pictures which shows my problem.
Pic 1 : I have binded two tables in skEntities
Pic 2: Intellisense not working
CODE:
Cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using Roll_set_MVC.Models;
namespace Roll_set_MVC
{
public class MyRoleProvider : RoleProvider
{
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(int username)
{
using (skEntities objContext = new skEntities())
{
var objUser = objContext.users.FirstOrDefault(x => x.UserID == username);
if (objUser == null)
{
return null;
}
else
{
string[] ret = objUser.Roles.Select(x => x.RoleName).ToArray();
return ret;
}
}
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override bool IsUserInRole(string username, string roleName)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}
I fixed my problem with the help of dotnetom. As he said I tried to access the Roles on User object and not on skEntities. Later I changed my code as
public override string[] GetRolesForUser(int username)
{
using (skEntities objContext = new skEntities())
{
var objUser = objContext.users.FirstOrDefault(x => x.UserID == username);
if (objUser == null)
{
return null;
}
else
{
string[] ret = objContext.Roles.Select(x => x.RoleName).ToArray();
return ret;
}
}
}
I changed objContext instead of objUser and It works fine now.

Execute action in other controller on 404

I'm trying to return a action "PageNotFound" that resides in my "Error"-controller.
public class BaseController : Controller
{
public BaseController()
{
}
public BaseController(IContentRepository contentRep, ILocalizedRepository localRep)
{
this._localRep = localRep;
this._contentRep = contentRep;
}
protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
{
return new HttpNotFoundResult(statusDescription);
}
protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
{
return new HttpUnauthorizedResult(statusDescription);
}
protected class HttpNotFoundResult : HttpStatusCodeResult
{
public HttpNotFoundResult() : this(null) { }
public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
}
protected class HttpUnauthorizedResult : HttpStatusCodeResult
{
public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
}
protected class HttpStatusCodeResult : ViewResult
{
public int StatusCode { get; private set; }
public string StatusDescription { get; private set; }
public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
public HttpStatusCodeResult(int statusCode, string statusDescription)
{
this.StatusCode = statusCode;
this.StatusDescription = statusDescription;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = this.StatusCode;
if (this.StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = this.StatusDescription;
}
this.ViewName = "PageNotFound"; // CONTROLLER MISSING
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
How can I modify it so it returns the "PageNotFound" action in the "Error"- controller?
A ViewResult is supposed to directly render a view (optionally passing a model and a layout). There's no controller involved in this process.
If you want to go through a controller you need to perform redirect, i.e. use RedirectToRouteResult instead of ViewResult.
In your example you are using this custom ViewResult directly inside some other controller. So that will be the controller that will render the error view.
I dont understand why you want to make a redirect. I would return 404
return HttpStatusCode(404);
And then use the approach described here: ASP.NET MVC 404 Error Handling to render the correct view. Benefit: your url is still the same, much easier for error handling and for the browser history.
Have you tried
return RedirectToAction("PageNotFound", "ControllerName");

How do you mock the session object collection using Moq

I am using shanselmann's MvcMockHelper class to mock up some HttpContext stuff using Moq but the issue I am having is being able to assign something to my mocked session object in my MVC controller and then being able to read that same value in my unit test for verification purposes.
My question is how do you assign a storage collection to the mocked session object to allow code such as session["UserName"] = "foo" to retain the "foo" value and have it be available in the unit test.
I started with Scott Hanselman's MVCMockHelper, added a small class and made the modifications shown below to allow the controller to use Session normally and the unit test to verify the values that were set by the controller.
/// <summary>
/// A Class to allow simulation of SessionObject
/// </summary>
public class MockHttpSession : HttpSessionStateBase
{
Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();
public override object this[string name]
{
get { return m_SessionStorage[name]; }
set { m_SessionStorage[name] = value; }
}
}
//In the MVCMockHelpers I modified the FakeHttpContext() method as shown below
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new MockHttpSession();
var server = new Mock<HttpServerUtilityBase>();
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session);
context.Setup(ctx => ctx.Server).Returns(server.Object);
return context.Object;
}
//Now in the unit test i can do
AccountController acct = new AccountController();
acct.SetFakeControllerContext();
acct.SetBusinessObject(mockBO.Object);
RedirectResult results = (RedirectResult)acct.LogOn(userName, password, rememberMe, returnUrl);
Assert.AreEqual(returnUrl, results.Url);
Assert.AreEqual(userName, acct.Session["txtUserName"]);
Assert.IsNotNull(acct.Session["SessionGUID"]);
It's not perfect but it works enough for testing.
Using Moq 3.0.308.2 here is an example of my account controller setup in my unit test:
private AccountController GetAccountController ()
{
.. setup mocked services..
var accountController = new AccountController (..mocked services..);
var controllerContext = new Mock<ControllerContext> ();
controllerContext.SetupGet(p => p.HttpContext.Session["test"]).Returns("Hello World");
controllerContext.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(_testEmail);
controllerContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
controllerContext.SetupGet(p => p.HttpContext.Response.Cookies).Returns(new HttpCookieCollection ());
controllerContext.Setup (p => p.HttpContext.Request.Form.Get ("ReturnUrl")).Returns ("sample-return-url");
controllerContext.Setup (p => p.HttpContext.Request.Params.Get ("q")).Returns ("sample-search-term");
accountController.ControllerContext = controllerContext.Object;
return accountController;
}
then within your controller method the following should return "Hello World"
string test = Session["test"].ToString ();
I've made a slightly more elaborate Mock than the answer posted by #RonnBlack
public class HttpSessionStateDictionary : HttpSessionStateBase
{
private readonly NameValueCollection keyCollection = new NameValueCollection();
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
public override object this[string name]
{
get { return _values.ContainsKey(name) ? _values[name] : null; }
set { _values[name] = value; keyCollection[name] = null;}
}
public override int CodePage
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override HttpSessionStateBase Contents
{
get { throw new NotImplementedException(); }
}
public override HttpCookieMode CookieMode
{
get { throw new NotImplementedException(); }
}
public override int Count
{
get { return _values.Count; }
}
public override NameObjectCollectionBase.KeysCollection Keys
{
get { return keyCollection.Keys; }
}
public Dictionary<string, object> UnderlyingStore
{
get { return _values; }
}
public override void Abandon()
{
_values.Clear();
}
public override void Add(string name, object value)
{
_values.Add(name, value);
}
public override void Clear()
{
_values.Clear();
}
public override void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public override bool Equals(object obj)
{
return _values.Equals(obj);
}
public override IEnumerator GetEnumerator()
{
return _values.GetEnumerator();
}
public override int GetHashCode()
{
return (_values != null ? _values.GetHashCode() : 0);
}
public override void Remove(string name)
{
_values.Remove(name);
}
public override void RemoveAll()
{
_values.Clear();
}
public override void RemoveAt(int index)
{
throw new NotImplementedException();
}
public override string ToString()
{
return _values.ToString();
}
public bool Equals(HttpSessionStateDictionary other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other._values, _values);
}
}
I just found a nice example of how the Oxite team fakes their HttpSessionState and maintains a SessionStateItemCollection collection within that fake. This should work just as well as a moq in my case.
EDIT:
URL for this example is http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065
I think you can set an expectation on the mock with a specific value it should return whatever. Mocks are not used as actual fakes but rather things that you can assert behavior on.
It sounds like you are actually looking for an adapter that you can wrap around the session that you can supply a different implementation during tests and during runtime it would return HttpContext Session items?
Does this make sense?
Thank you, #RonnBlack for your solution! In my case, I kept getting this exception because Session.SessionID was null:
System.NotImplementedException was unhandled by user code
HResult=-2147467263
Message=The method or operation is not implemented.
Source=System.Web
StackTrace:
at System.Web.HttpSessionStateBase.get_SessionID()
To solve this problem I implement #RonnBlack's code this way using the Moq Mock<HttpSessionStateBase> instead of his MockHttpSession:
private readonly MyController controller = new MyController();
[TestFixtureSetUp]
public void Init()
{
var session = new Mock<HttpSessionStateBase>();
session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString());
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var server = new Mock<HttpServerUtilityBase>();
// 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
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.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(p => p.Request.Url).Returns(new Uri("http://www.mytesturl.com"));
var queryString = new NameValueCollection { { "code", "codeValue" } };
context.SetupGet(r => r.Request.QueryString).Returns(queryString);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
}
For details, please see http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase
Just for Session easier way is to create Session object in parent class and use it like this
public class DalBl : IDalBl
{
public dynamic Session
{
get { return HttpContext.Current.Session; }
}
}
and in unitTest
var session = new Dictionary<string, object>();
var moq = new Moq.Mock<IDalBl>();
moq.Setup(d => d.Session).Returns(session);

Resources