I am attempting to mock a controller's Session variable using Moq. What I keep running into is that the Session is visible from the test, but null inside the actual controller.
Test code:
[TestMethod]
public void SessionTest()
{
var controller = new BaseController();
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(cc => cc.HttpContext.Session["user"]).Returns(new User());
controller.ControllerContext = controllerContext.Object;
User currentUser = controller.CurrentUser; //fails (throws NullReferenceException)
User currentUser = (User)controller.Session["test"]; //works
}
Controller code:
public User CurrentUser
{
get
{
return (User)Session["user"]; //HttpContext is null at this point
}
}
The code above follows the most common pattern I've seen (several SO postings, the MVC 2 in Action Ch. 7 example, etc.), but still doesn't work. Inside the controller, the ControllerContext is null, as is the HttpContext. What am I doing wrong?
It must have been a configuration issue caused by adding a unit test project to an already-existing project, because I just created a new MVC 3 project, checked the 'Create Unit Test Project', added all of my controllers, models, etc. from the old project to the new one, and it's working just fine.
Related
I'm testing the Account/Loggon action using the built-in testing tool of Visual Studio 2010 and the class library from this article to create a fake controller context.
When I run the test method, this code line:
FormsAuthentication.SetAuthCookie(username, false);
throws an exception: Object reference not set to an instance of an object
To test the loggon action, I think I should create a controller with a fake controller context that has a cookie collection. Here is my testing code block:
AccountController controller = new AccountController();
var cookies = new HttpCookieCollection();
controller.ControllerContext = new FakeControllerContext(controller, cookies);
ActionResult result = controller.RemoteLogOn(username, password);
I'm not sure if this is the right way, but this is what we do, and it works.
Instead of directly using FormsAuthentication.SetAuthCookie, abstract it into an interface, e.g IFormsAuthenticationService, and implement as per regular.
Accept that in your MVC controllers where required, e.g:
public AccountController(IFormsAuthenticationService formsAuthenticationService)
{
_formsAuthenticationService = formsAuthenticationService; // should use DI here
}
public ActionResult LogOn(string username, string pw)
{
if (yourLogicWhichChecksPw)
_formsAuthenticationService.SetAuthCookie(username, false);
return RedirectToAction("Index");
}
Then in your unit-test, use something like Moq to fake out the interface.
var username = "blah";
var pw = "blah";
var fakesFormsAuth = new Mock<IFormsAuthenticationService>();
fakeFormsAuth.Verify(x => x.SetAuthCookie(username, false), Times.AtLeastOnce());
var controller = new AccountController(fakedFormsAuth.Object);
controller.LogOn(username, pw);
The reason for mocking this is because there is absolutely no need to unit-test Forms Authentication. It's a built-in, well tested and stable part of the ASP.NET framework. That's why we mock things where we don't care about the underlying implementation, instead we only test that certain conditions were met (it was called, exception was thrown, some variable was set, etc).
Test your own code, not the mechanics of .NET.
As for Stephen Walther's article, that's more for faking the RequestContext when certain code your testing expects data in the Request. Such as the User.Identity, Request.IsAuthenticated, Form variables, etc. That's where you need to fake the context, such as the following code:
public ActionResult Save(SomeModel)
{
var user = Request.User.Identity; // this will be null, unless you fake the context.
}
I'm writing an MVC app.
If I have a piece of code:
#if (User.IsInRole("Administrator")) {
#Html.DropDownListFor(...)
}
So the dropdownlistfor is only visible or even there for administrators.
How do you unit test that this is happening?
This is what I would do:
Create a stub for the User object (of type IPrincipal)
Create a stub for the Request object (of type HttpRequestBase) with the stubbed User object
Inject the stubbed request object to your request context.
See example below (using Rhino mocks)
var CurrentUser = MockRepository.GenerateStub<IPrincipal>();
CurrentUser.Stub(u => u.IsInRole("Administrator")).Return(true);
var context = MockRepository.GenerateStub<HttpContextBase>();
var requestContext = new RequestContext(context, new RouteData());
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.User = CurrentUser;
context.Stub(c => c.Request).Return(request);
controller = new YourController();
controller.ControllerContext = new ControllerContext(requestContext, controller);
var view = controller.DoAction() as ViewResult;
That would help you setup the controller and the current user with all required roles.
I personally wouldn't unit test the view as it is supposed to be thin and dump. Most of your stuff should happen in the controller/business layer.
If you want to unit test your views, I would suggest checking out the Razor Single File Generator. This will allow you to pre-compile your views, and to create unit tests as well.
I am trying to learn Moq but it is proving somewhat difficult.
If I want to implement some basic tests using nUnit and Moq for the account controller in a new MVC3 project, how would I go about it?
Im used to the entity framework. but not building interfaces for it.
edit:
I understand the theory of it all and the need to do it, but implementing it is confusing me
I have been using Entity Code generator (dbContext) to generate code I can use for interfaces
Ok, here is a good test: When you register a new user, you want to make sure that he will be automatically signed in the site, so he doest not need to type his username and password again.
The test would be something like this:
public void AutomaticallySignedInAfterRegistering()
{
var membershipService = new Mock<IMembershipService>();
var formsService = new Mock<IFormsAuthenticationService>();
RegisterModel newUser = new RegisterModel();
newUser.UserName = "John"
newUser.Email = "john#somewhere.com"
newUser.Password = "p#ss";
newUser.ConfirmPassword = "p#ss";
membershipService.Setup(x => x.CreateUser("John", "p#ss", "john#somewhere.com")).Returns(MembershipCreateStatus.Success);
AccountController controller = new AccountController();
controller.FormsService = formsService.Object;
controller.MembershipService = membershipService.Object;
controller.Register(newUser);
formsService.Verify(x => x.SignIn("John", false), Times.Once());
}
The key here is the Verify method. It works just like an Assert. In this case you are verifing that the method SignIn was called exactly once. This is an example of how to use mocks to check if the Account Controller is working as expected.
I'm using Moq to help in testing my ASP.NET MVC2 application.
Problem: ArgumentException was unhandled by user code. Unable to obtain public key for StrongNameKeyPair
This code has been adapted from Scott Hanselman's NerdDinner1.
HomeController CreateHomeControllerAs(string userName)
{
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName); // fails here
mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
var controller = new HomeController();
controller.ControllerContext = mock.Object;
return controller;
}
[TestMethod]
public void should_be_able_to_get_to_index_page_logged_in()
{
HomeController controller = CreateHomeControllerAs("dave");
}
Using Moq referenced... VS2010 under WinXP.
There's nothing wrong with your code. I've just tested it and it worked fine. The problem is with the Moq assembly. You need to grant specific permissions to the C:\Documents and Settings\AllUsers\ApplicationData\Microsoft\Crypto\RSA\MachineKeys folder. Checkout this discussion.
Also right click on the Moq.dll in Windows Explorer and in the properties make sure that it is not locked. When you download some DLL from the internet Windows automatically applies restricted permissions to it.
I have a controller that implements a simple Add operation on an entity and redirects to the Details page:
[HttpPost]
public ActionResult Add(Thing thing)
{
// ... do validation, db stuff ...
return this.RedirectToAction<c => c.Details(thing.Id));
}
This works great (using the RedirectToAction from the MvcContrib assembly).
When I'm unit testing this method I want to access the ViewData that is returned from the Details action (so I can get the newly inserted thing's primary key and prove it is now in the database).
The test has:
var result = controller.Add(thing);
But result here is of type: System.Web.Mvc.RedirectToRouteResult (which is a System.Web.Mvc.ActionResult). It doesn't hasn't yet executed the Details method.
I've tried calling ExecuteResult on the returned object passing in a mocked up ControllerContext but the framework wasn't happy with the lack of detail in the mocked object.
I could try filling in the details, etc, etc but then my test code is way longer than the code I'm testing and I feel I need unit tests for the unit tests!
Am I missing something in the testing philosophy? How do I test this action when I can't get at its returned state?
I am using MVC2 RC2 at the moment and the answer from rmacfie didn't quite work for me but did get me on the right track.
Rightly or wrongly I managed to do this in my test instead:
var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model);
actionResult.RouteValues["action"].should_be_equal_to("Index");
actionResult.RouteValues["controller"].should_be_equal_to("Logon");
Not sure if this will help someone but might save you 10 minutes.
There is MVC Contrib TestHelper that are fantastic for testing most of the ActionResult
You can get it here:
http://mvccontrib.codeplex.com/wikipage?title=TestHelper
Here is an example of the syntax:
var controller = new TestController();
controller.Add(thing)
.AssertActionRedirect()
.ToAction<TestController>(x => x.Index());
To test if the data has been persisted successfully, you should maybe ask your database directly, I don't know if you're using an ORM or something, but you should do something to get the last insterted item in your database, then compare with the value you provided to your Add ActionResult and see if this is ok.
I don't think that testing your Details ActionResult to see if your data is persisted is the right approach. That would not be an unit test, more a functional test.
But you should also unit test your Details method to make sure that your viewdata object is filled with the right data coming from your database.
You seem to be doing way too much for a unit test. The validation and data access would typically be done by services that you call from the controller action. You mock those services and only test that they were called properly.
Something like this (using approximate syntax for Rhino.Mocks & NUnit):
[Test]
public void Add_SavesThingToDB()
{
var dbMock = MockRepository.GenerateMock<DBService>();
dbMock.Expect(x => x.Save(thing)).Repeat.Once();
var controller = new MyController(dbMock);
controller.Add(new Thing());
dbMock.VerifyAllExpectations();
}
[Test]
public void Add_RedirectsAfterSave()
{
var dbMock = MockRepository.GenerateMock<DBService>();
var controller = new MyController(dbMock);
var result = (RedirectToRouteResult)controller.Add(new Thing());
Assert.That(result.Url, Is.EqualTo("/mynew/url"));
}
I have a static helper method that tests redirection.
public static class UnitTestHelpers
{
public static void ShouldEqual<T>(this T actualValue, T expectedValue)
{
Assert.AreEqual(expectedValue, actualValue);
}
public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues)
{
RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues;
var expectedValues = new RouteValueDictionary(expectedRouteValues);
foreach (string key in expectedValues.Keys)
{
Assert.AreEqual(expectedValues[key], actualValues[key]);
}
}
}
Then creating a redirection test is very easy.
[Test]
public void ResirectionTest()
{
var result = controller.Action();
result.ShouldBeRedirectionTo(
new
{
controller = "ControllerName",
action = "Index"
}
);
}