Unit testing controller using MOQ . How to mock httpcontext - asp.net-mvc

I am trying to test my Account controller by using Moq here is what i have done
Controller
private readonly IWebSecurity _webSecurity;
public AccountController(IWebSecurity webSecurity)
{
this._webSecurity = webSecurity;
}
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _webSecurity.login(model))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
IWebSecurity
public interface IWebSecurity
{
bool login(LoginModel model);
}
public class WebSecurity : IWebSecurity
{
public bool login(LoginModel model)
{
return WebMatrix.WebData.WebSecurity.Login(model.UserName, model.Password, model.RememberMe);
}
}
MyTestClass
[AfterScenario]
public void OnAfterScenario() {
mockRepository.VerifyAll();
}
LoginModel loginModel;
AccountController _controller;
#region Initializing Mock Repository
readonly Mock<IWebSecurity> mockRepository = new Mock<IWebSecurity>(MockBehavior.Loose);
ViewResult viewResult;
#endregion
[Given]
public void Given_Account_controller()
{
_controller = new AccountController(mockRepository.Object);
}
[When]
public void When_login_is_called_with_LoginModel(Table table)
{
loginModel = new LoginModel
{
UserName = table.Rows[0][1],
Password = table.Rows[1][1]
};
mockRepository.Setup(x => x.login(loginModel)).Returns(true);
viewResult = (ViewResult)_controller.Login(loginModel, "/");
}
[Then]
public void Then_it_should_validate_LoginModel()
{
Assert.IsTrue(_controller.ModelState.IsValid);
}
[Then]
public void Then_it_should_return_default_view()
{
Assert.AreEqual(viewResult.ViewName, "Index");
}
But my test is failing and its giving expection when if come to Url.IsLocal in Redirect to Local method . so i think here is should mock my httpcontextbase and httpcontextrequestbase .
But don't know how to mock that .
Thanks in advance

You should mock the HttpContext. I wrote this helper to do this kind of things
public static Mock<HttpContextBase> MockControllerContext(bool authenticated, bool isAjaxRequest)
{
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.HttpMethod).Returns("GET");
request.SetupGet(r => r.IsAuthenticated).Returns(authenticated);
request.SetupGet(r => r.ApplicationPath).Returns("/");
request.SetupGet(r => r.ServerVariables).Returns((NameValueCollection)null);
request.SetupGet(r => r.Url).Returns(new Uri("http://localhost/app", UriKind.Absolute));
if (isAjaxRequest)
request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "XMLHttpRequest" } });
var server = new Mock<HttpServerUtilityBase>();
server.Setup(x => x.MapPath(It.IsAny<string>())).Returns(BasePath);
var response = new Mock<HttpResponseBase>();
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((String url) => url);
var session = new MockHttpSession();
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Setup(c => c.Request).Returns(request.Object);
mockHttpContext.Setup(c => c.Response).Returns(response.Object);
mockHttpContext.Setup(c => c.Server).Returns(server.Object);
mockHttpContext.Setup(x => x.Session).Returns(session);
return mockHttpContext;
}
public class MockHttpSession : HttpSessionStateBase
{
private readonly Dictionary<string, object> sessionStorage = new Dictionary<string, object>();
public override object this[string name]
{
get { return sessionStorage.ContainsKey(name) ? sessionStorage[name] : null; }
set { sessionStorage[name] = value; }
}
public override void Remove(string name)
{
sessionStorage.Remove(name);
}
}
and in a test method you use it like that
private AccountController GetController(bool authenticated)
{
var requestContext = new RequestContext(Evoltel.BeniRosa.Web.Frontend.Tests.Utilities.MockControllerContext(authenticated, false).Object, new RouteData());
var controller = new CofaniController(cofaniRepository.Object, categorieRepository.Object, emailService.Object, usersService.Object)
{
Url = new UrlHelper(requestContext)
};
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = requestContext
};
return controller;
}
[Test]
public void LogOn_Post_ReturnsRedirectOnSuccess_WithoutReturnUrl()
{
AccountController controller = GetController(false);
var httpContext = Utilities.MockControllerContext(false, false).Object;
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
LogOnModel model = new LogOnModel()
{
UserName = "someUser",
Password = "goodPassword",
RememberMe = false
};
ActionResult result = controller.LogOn(model, null);
Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
}
Hope it helps

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"]);
}
}

Related

Cannot create instance of repository object in action filter MVC

public interface IUserRepository : IBaseRepository<user>
{
user GetUser(int userId);
user Get(string Email);
}
public class UserRepository : BaseRepository<user>, IUserRepository
{
public UserRepository(IUnitOfWork unit) : base(unit)
{
}
public void Dispose()
{
throw new NotImplementedException();
}
public user GetUser(int userId)
{
return dbSet.Where(x => x.ID == userId).FirstOrDefault();
}
public user Get(string Email)
{
var obj = dbSet.Where(s => s.Email == Email).FirstOrDefault();
return obj;
}
}
And I am using the repository in my controller as below
public class AccountController : Controller
{
private readonly ApplicationUserManager UserManager;
private readonly ApplicationSignInManager SignInManager;
private readonly IAuthenticationManager AuthenticationManager;
private readonly IUnitOfWork uow;
private readonly UserRepository userrepo;
public AccountController(UserRepository _userrepo, ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authenticationManager,IUnitOfWork _uow)
{
this.UserManager = userManager;
this.SignInManager = signInManager;
this.AuthenticationManager = authenticationManager;
this.uow = _uow;
userrepo = _userrepo;
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid )
{
var user = UserManager.FindByEmail(model.Email);
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
if (result)
{
var myUser = userRepo.Get(user.Id);
if (myUser.SubscriptionStatus == 1)
{
return RedirectToAction("ChangePassword", "Manage", new { ReturnUrl = returnUrl });
}
else
{
return RedirectToAction("Index","Admin");
}
}
}
}
And this is my action filter
public class CheckFirstLoginAttribute : ActionFilterAttribute
{
private readonly ApplicationUserManager UserManager;
private readonly IUnitOfWork uow;
private readonly UserRepository userrepo;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string uName = HttpContext.Current.User.Identity.Name;
if (!string.IsNullOrEmpty(uName))
{
//var user = UserManager.FindByEmailAsync(uName);
//The above & below lines are not creating instance of the UserManager & UserRepository object, it is always null
user cUser= userrepo.GetUserId(uName);
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Manage",
action = "ChangePassword"
}));
}
}
}
And I am using Unity for the dependency injection
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
private static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<MyDbContext>();
container.RegisterType<UserRepository>();
container.RegisterType<IUnitOfWork, UnitOfWork>();
container.RegisterType<ApplicationDbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
container.RegisterType<HomeController>();
container.RegisterType<AccessCodeController>();
container.RegisterType<AdminController>();
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
container.RegisterType<IUserStore<MyUser, int>, UserStore<MyUser, MyRole, int, MyUserLogin, MyUserRole, MyUserClaim>>(
new InjectionConstructor(typeof(ApplicationDbContext)));
}
}
How can I create the instance of the repository class and access the Get(string email) method. So that I can check the subscription status from the database.
I tried many ways and always failed to create the instance.
Kindly Help me.
Thanks
Tarak
public class CheckFirstLoginAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string uName = HttpContext.Current.User.Identity.Name;
if (!string.IsNullOrEmpty(uName))
{
//Here is how I could get the instance of the repository
//I had to register the repository type and then
//I resolved it to obtain the object of the repository and
//access the methods in the repository
var container = new UnityContainer();
container.RegisterType<IUnitOfWork, UnitOfWork>();
container.RegisterType<UserRepository>();
UserRepository repo = container.Resolve<UserRepository>();
user cUser = repo.GetUserId(uName);
if (cUser.SubscriptionStatus == 1)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Manage",
action = "ChangePassword"
}));
}
}
}
}

ReturnsAsync in Moq is not working

Here is my code
public interface IUserManager
{
Task<int> PostUser(User user);
IQueryable<User> GetUserById(long userId);
}
public class UserManager : IUserManager
{
public UserManager(DataContext context)
{
this.DataContext = context;
}
public async Task<int> PostUser(User user)
{
this.DataContext.User.Add(user);
return await this.DataContext.SaveChangesAsync().ConfigureAwait(false);
}
public IQueryable<User> GetUserById(long userId)
{
return this.DataContext.User
.Where(userNotes => userNotes.UserId == userId).AsQueryable();
}
}
Controller:
public class UserController : BaseController
{
private readonly IUserManager userManager;
public UserController()
{
this.userManager = new UserManager();
}
public UserController(IUserManager userManager)
{
this.userManager = userManager;
}
[EnableQuery]
public IQueryable<User> Get([FromODataUri]long userId)
{
return this.userManager.GetUserById(userId);
}
public HttpResponseMessage Post(User user)
{
if (userNote == null || !ModelState.IsValid)
{
return this.BuildErrorResponse(ResponseCodes.INVALID_MISSING_INPUTS);
}
if (this.userManager.PostUser(user).Result <= 0)
{
return this.BuildErrorResponse(ResponseCodes.USER_ADD_FAILED);
}
return this.BuildSuccessResponse<User>(ResponseCodes.USER_ADDED, user);
}
}
Moq test:
[TestClass]
public class UnitTest
{
IUserManager userManagerMock;
Mock<IUserManager> iUserManagerMock;
[TestInitialize]
public void Setup()
{
//.. setup variables and mock data
userManagerMock = new UserManager(ContextMock.Object);
iUserManagerMock.Setup(u => u.PostUser(It.IsAny<User>()))
.ReturnsAsync(1);
}
[TestMethod]
public void Post()
{
var controller = new UserController(userManagerMock); //userNotesManagerMock will have the mock data //and mock methods setup
var httpResponse = controller.Post(userPostMock); //userPostMock will have mock data to save
//Assert.AreEqual(HttpResponseMessage, result);
}
}
I wrote a post method as you'd see here. I've a Get method as well which is working perfectly with mocking data.
But when I debug through the Post, the data the following statement is always returns ZERO instead of 1, which I've set up in the ReturnsAsync.
this.userManager.PostUser(user).Result <= 0 //this always gives me zero
What's wrong with the post ? Am I doing anything wrong ?
Can anyone shed some light on this
EDIT
Here is the GET version of the Test method and setup
[TestInitialize]
public void Setup()
{
//dummy data setup
UserMock = new List<User>
{
new User { //blah blah properties }
}.AsQueryable();
//context setup
dbSetUserMock = new Mock<IDbSet<User>>();
dbSetUserMock.Setup(m => m.Provider).Returns(UserMock.Provider);
dbSetUserMock.Setup(m => m.Expression).Returns(UserMock.Expression);
dbSetUserMock.Setup(m => m.ElementType).Returns(UserMock.ElementType);
dbSetUserMock.Setup(m => m.GetEnumerator()).Returns(UserMock.GetEnumerator());
UserContextMock = new Mock<DataContext>();
UserContextMock.Setup(s => s.User).Returns(dbSetUserMock.Object);
//inject the context to manager
UserManagerMock = new UserManager(UserContextMock.Object);
iUserManagerMock = new Mock<IUserManager>();
iUserManagerMock.Setup(notes => notes.PostUserNote(It.IsAny<User>()))
.ReturnsAsync(1);
}
[TestMethod]
public void Get_User_Success()
{
var controller = new UserController(UserManagerMock);
var values = controller.Get(100);
Assert.AreEqual(100, values.FirstOrDefault().UserId);
}
Get_User_Success works well now with the dummy data set and I'm able to pass the test without iUserManagerMock object. Reason being I want to execute the code inside of GetUser() method in Manager class.
And I've another question over here. As I'm setting the expected result to be 1 in the ReturnAsync for Post action. How do I build a test method for failure case scenario as the return from the POST method will still be 1 for failure case ?
You are mixing up the set up. If the method under test is the UserController.Post and you want to mock the UserManager via the IUserManager interface, then you don't need to new one up manually.
[TestClass]
public class UnitTest {
[TestMethod]
public void UserController_Should_Post_User_Ok() {
//Arrange
var userPostMock = new User { UserId = 100 };
var userManagerMock = new Mock<IUserManager>();
userManagerMock.Setup(u => u.PostUser(It.IsAny<User>())).ReturnsAsync(1);
var controller = new UserController(userManagerMock.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var httpResponse = controller.Post(userPostMock);
//Assert
Assert.AreEqual(System.Net.HttpStatusCode.OK, httpResponse.StatusCode);
}
[TestMethod]
public void UserController_Should_Post_User_Fail() {
//Arrange
var userPostMock = new User { UserId = 100 };
var userManagerMock = new Mock<IUserManager>();
userManagerMock.Setup(u => u.PostUser(It.IsAny<User>())).ReturnsAsync(0);//Replicating a failed post
var controller = new UserController(userManagerMock.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var httpResponse = controller.Post(userPostMock);
//Assert
Assert.AreEqual(System.Net.HttpStatusCode.InternalServerError, httpResponse.StatusCode);
}
[TestMethod]
public void UserManager_Should_Get_User_Success() {
//Arrange
var userMock = new List<User>
{
new User (){ UserId=100 }
}.AsQueryable();
//context setup
var dbSetUserMock = new Mock<IDbSet<User>>();
dbSetUserMock.Setup(m => m.Provider).Returns(userMock.Provider);
dbSetUserMock.Setup(m => m.Expression).Returns(userMock.Expression);
dbSetUserMock.Setup(m => m.ElementType).Returns(userMock.ElementType);
dbSetUserMock.Setup(m => m.GetEnumerator()).Returns(userMock.GetEnumerator());
var userContextMock = new Mock<DataContext>();
userContextMock.Setup(s => s.User).Returns(dbSetUserMock.Object);
//inject the context to manager
var userManagerMock = new UserManager(userContextMock.Object);
//Act
var values = userManagerMock.GetUserById(100);
//Assert
Assert.AreEqual(100, values.FirstOrDefault().UserId);
}
}

Trying to register a user from another controller other than AccountController gives an error

Totally messed up my last question so posting a new one.
MyTestController:
[HttpPost]
public async Task<ActionResult> Index(MyTestViewModel viewModel)
{
if (ModelState.IsValid)
{
AccountController ac = new AccountController();
var user = new ApplicationUser()
{
UserName = viewModel.Email
};
var result = await ac.UserManager.CreateAsync(user, viewModel.Password);
if (result.Succeeded)
{
await ac.SignInAsync(user, isPersistent: true);
}
else
{
ac.AddErrors(result);
}
The SignInAsync method in AccountController (changed this from private to public):
public async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
While trying to register the user it gives me the following error:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 411: get
Line 412: {
Line 413: return HttpContext.GetOwinContext().Authentication;
Line 414: }
Line 415: }
Those lines in AccountController:
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
Everything in the AccountController is default MVC 5 app stuff.
It is not possible to call these methods from another controller, like in my example above?
And why am I getting a NullReferenceException on line 413?
Calling a Controller method from another Controller is difficult because of properties like the HttpContext which need to be properly initialized. This is usually done by the MVC framework which creates the controller using a ControllerFactory and at some point during this process the protected method Initialize is called on the controller which ensures that the HttpContext property is set.
This is why you get the exception on line 413, because the Initialize method hasn't been called on the controller you created using the new operator.
I think it would be easier to refactor out the functionality you want to share.
E.g. if both AccountController and your MyTestController holds a reference to something like this
public class AccountManager
{
public UserManager<ApplicationUser> UserManager { get; private set; }
public HttpContextBase HttpContext { get; private set; }
public AccountManager()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountManager(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public void Initialize(HttpContextBase context)
{
HttpContext = context;
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
public async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
}
You would then modify the AccountController like this:
public class AccountController : Controller
{
public AccountController()
: this(new AccountManager())
{
}
public AccountController(AccountManager accountManager)
{
AccountManager = accountManager;
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
AccountManager.Initialize(this.HttpContext);
}
public UserManager<ApplicationUser> UserManager
{
get
{
return AccountManager.UserManager;
}
}
public AccountManager AccountManager { get; private set; }
And your MyTestController would be like this:
public class MyTestController : Controller
{
public MyTestController ()
: this(new AccountManager())
{
}
public MyTestController (AccountManager accountManager)
{
AccountManager = accountManager;
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
AccountManager.Initialize(this.HttpContext);
}
public AccountManager AccountManager { get; private set; }
[HttpPost]
public async Task<ActionResult> Index(MyTestViewModel viewModel)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser()
{
UserName = viewModel.Email
};
var result = await AccountManager.UserManager.CreateAsync(user, viewModel.Password);
if (result.Succeeded)
{
await AccountManager.SignInAsync(user, isPersistent: true);
}
else
{
AddErrors(result); //don't want to share this a it updates ModelState which belongs to the controller.
}
Update:
Had to make som minor changes:
I had to change the UserManager property since the Dispose method uses the setter method:
private UserManager<ApplicationUser> _userManager;
public UserManager<ApplicationUser> UserManager
{
get { return AccountManager.UserManager; }
private set { _userManager = value; }
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
I had to add the AddErrors method to MyTestController (as you pointed out that we don't want to share that method):
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
I re-added this line to the AccountManager property in the AccountManager class (not really related to the question but I had it in my project)
UserManager.UserValidator = new UserValidator(UserManager) { AllowOnlyAlphanumericUserNames = false };
For me it working like a charm after setting the current ControllerContext to AccountControllerContext. Not sure if there are any drawbacks with this approach.
//This is employee controller class
public ActionResult Create([Bind(Include = "EmployeeId,FirstName,LastName,DOJ,DOB,Address,City,State,Mobile,Landline,ReportsTo,Salary")] Employee employee)
{
if (ModelState.IsValid)
{
AccountController accountController = new AccountController();
accountController.ControllerContext = this.ControllerContext;
//accountController.UserManager;
var userId = accountController.RegisterAccount(new RegisterViewModel { Email = "temp#temp.com", Password = "Pravallika!23" });
if (!string.IsNullOrEmpty(userId))
{
employee.UserId = userId;
employee.CreatedBy = User.Identity.GetUserId();
db.Employees.Add(employee);
db.SaveChanges();
return RedirectToAction("Index");
}
}
//customized method in AccountController
public string RegisterAccount(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = UserManager.Create(user, model.Password);
//to add roles
//UserManager.AddToRole(user.Id, "Admin");
if (result.Succeeded)
{
return user.Id;
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed
return null;
}

How do I stub the HttpSessionState in MVC RC1 with rhino mocks?

I'm trying to take advantage of the recent ControllerContext refactoring in asp.net mvc rc1. I should be able to stub the session rather simply but I keep getting a System.NullReferenceException on line 2 when running the following code:
var mockContext = MockRepository.GenerateStub<ControllerContext>();
mockContext.Stub(x => x.HttpContext.Session["MyKey"]).Return("MyValue");
What am I doing wrong?
Edit: I just verified I have the latest version of rhino as of this post.
You need to mock HttpContext too to make this working.
I'm using a mock of HttpContext for this:
public class HttpContextMock
{
private readonly HttpContextBase _contextBase;
private readonly HttpRequestBase _requestBase;
private readonly HttpResponseBase _responseBase;
private readonly HttpSessionStateBase _sessionStateBase;
private readonly HttpServerUtilityBase _serverUtilityBase;
public HttpContextBase Context { get { return _contextBase; } }
public HttpRequestBase Request { get { return _requestBase; } }
public HttpResponseBase Response { get { return _responseBase; } }
public HttpSessionStateBase Session { get { return _sessionStateBase; } }
public HttpServerUtilityBase Server { get { return _serverUtilityBase; } }
public HttpContextMock()
{
_contextBase = MockRepository.GenerateStub<HttpContextBase>();
_requestBase = MockRepository.GenerateStub<HttpRequestBase>();
_responseBase = MockRepository.GenerateStub<HttpResponseBase>();
_sessionStateBase = MockRepository.GenerateStub<HttpSessionStateBase>();
_serverUtilityBase = MockRepository.GenerateStub<HttpServerUtilityBase>();
_contextBase.Stub(x => x.Request).Return(_requestBase);
_contextBase.Stub(x => x.Response).Return(_responseBase);
_contextBase.Stub(x => x.Session).Return(_sessionStateBase);
_contextBase.Stub(x => x.Server).Return(_serverUtilityBase);
_requestBase.Stub(x => x.IsAuthenticated).Return(true);
_contextBase.User = new GenericPrincipal(new GenericIdentity("a#a.a"),
new string[] {"Admin"});
}
}
And in SetUp of test I create an instance of a controller:
[SetUp]
public override void TestSetUp()
{
base.TestSetUp();
repository = MockRepository.GenerateMock<IFooRepository>();
controller = new FooController()
{
FooRepository = repository,
UserRepository = userMockRepository
};
controller.ControllerContext = new ControllerContext(context.Context, new RouteData(), controller);
}
And all is working fine, I can add parameters to session and many other things.
Hope this helps.

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