Reputation: 86987
In my simple Index()
ActionMethod I reference the User.Identity property. So, I thought that I need to mock that.
So i create a some mock HomeController
and use that in my unit test. When I do that, the ActionMethod
returns null as the view. When I replace the mock'd controller with a concrete instance (and of course comment out any reference to User.Identity
) then the correct view is returned.
eg.
// Arrange.
var homeController = Mock<HomeController>(..);
homeController.Setup(x => x.User).Returns(new GenericPrincipal(..));
// Act.
var result = homeController.Index();
// Assert.
Assert.IsNotNull(result); // This fails here. result is NULL.
but when I do this (and comment out any User
reference), it works...
// Arrange.
var homeController = new HomeController(..);
// Act.
var result = homeController.Index();
// Assert.
Assert.IsNotNull(result); // Tick!
Any ideas why this is?
Upvotes: 2
Views: 1213
Reputation: 12573
I think that your Index method is probably virtual, which causes Moq to replace that function with a mocked function. In order to prevent that, you need to set the CallBase property on the mock.
However, I agree with the other replies that you should not be mocking your controller, but you should mock your dependency.
(an even simpler method is to create a specialized model binder that can fetch the principal from the HttpContext, then you can just pass the principal as input parameter to your method)
Upvotes: 1
Reputation: 2868
To be honest, this looks like a very strange test because you're mocking the system under test (SUT), in other words the HomeController
. Usually one would mock the dependencies of the SUT, set the expectations on the mock and inject the mock into the SUT to confirm that it is properly interacting with its dependencies.
When you create a mock of the HomeController
, Moq is creating a class that inherits from HomeController and overrides the virtual method Index
. So when you call Index
on the mock you're not calling the implementation of Index
you've defined in the HomeController
class but the overridden one. Since you've not explicitly Setup
the method on the mock it will return the default value, in this case null
.
In your second test you're calling the actual implementation of Index
because you're constructing an actual instance of the HomeController
class. If you call GetType()
on the instance of the mock object then you'll see that it's an instance of a proxy that derives from HomeController
which intercepts calls to the publicly defined, overridable methods on the base class (which is a large part of the purpose of a mock object).
Upvotes: 1
Reputation: 125518
I think you should be mocking a HttpContext for the controller to work with. I provided one on another answer that you could use. As Steve Rowbotham says, you should be mocking the dependencies of the system under test (i.e. the dependencies of the controller) and not mocking the system under test itself; you want to test the real version of the controller not a mock :)
Using the HttpContextBase
class from the link, you would simply do the following in your test
var controllerToTest = new HomeController();
var context = new MockHttpContextBase(controllerToTest);
// do stuff that you want to test e.g. something goes into session
Assert.IsTrue(context.HttpContext.Session.Count > 0);
You may go a step further and create Setup and TearDown methods to set the controller up with a mocked context.
Upvotes: 2
Reputation: 1039110
There are some strange things in your unit test. You are unit testing a controller and yet you are mocking the creation of the object under test: var homeController = Mock<HomeController>(..);
which is incorrect.
Here's the correct way to inject a mocked user into a controller that you are willing to unit test:
// arrange
var sut = new HomeController();
var user = new GenericPrincipal(new GenericIdentity("foo"), null);
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(x => x.User).Returns(user);
var context = new ControllerContext(new RequestContext(httpContext.Object, new RouteData()), sut);
sut.ControllerContext = context;
// act
var actual = sut.Index();
// assert
...
Upvotes: 3