[英]When I mock my ASP.NET MVC controller, my ActionMethod returns no view. Why?
In my simple Index()
ActionMethod I reference the User.Identity property. 在我简单的
Index()
ActionMethod中,我引用了User.Identity属性。 So, I thought that I need to mock that. 因此,我认为我需要对此进行嘲笑。
So i create a some mock HomeController
and use that in my unit test. 所以我创建了一个模拟的
HomeController
并在我的单元测试中使用了它。 When I do that, the ActionMethod
returns null as the view. 当我这样做时,
ActionMethod
返回null作为视图。 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. 当我用具体实例替换模拟控制器时(当然,请注释掉对
User.Identity
所有引用),然后返回正确的视图。
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... 但是当我这样做(并注释掉任何
User
参考)时,它可以工作...
// Arrange.
var homeController = new HomeController(..);
// Act.
var result = homeController.Index();
// Assert.
Assert.IsNotNull(result); // Tick!
Any ideas why this is? 任何想法为什么会这样?
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>(..);
您正在对控制器进行单元测试,但是正在
var homeController = Mock<HomeController>(..);
被测对象的创建: 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
...
I think you should be mocking a HttpContext for the controller to work with. 我认为您应该模拟HttpContext以便控制器使用。 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 (ie the dependencies of the controller) and not mocking the system under test itself;
正如史蒂夫·罗伯瑟姆 ( Steve Rowbotham)所说,您应该模拟被测系统的依赖性(即控制器的依赖性),而不是模拟被测系统本身。 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 使用链接中的
HttpContextBase
类,您只需在测试中执行以下操作
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. 您可以更进一步,并创建Setup和TearDown方法以使用模拟的上下文设置控制器。
To be honest, this looks like a very strange test because you're mocking the system under test (SUT), in other words the HomeController
. 老实说,这看起来像是一个非常奇怪的测试,因为您正在模拟被测系统(SUT),换句话说就是
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. 通常,将模拟SUT的依赖关系,设置对模拟的期望,然后将模拟注入SUT以确认其与依赖关系正确交互。
When you create a mock of the HomeController
, Moq is creating a class that inherits from HomeController and overrides the virtual method Index
. 当您创建
HomeController
的模拟时,Moq将创建一个从HomeController继承并覆盖虚拟方法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. 因此,当您在模拟对象上调用
Index
时,您不是在调用HomeController
类中定义的Index
的实现,而是调用已重写的实现。 Since you've not explicitly Setup
the method on the mock it will return the default value, in this case null
. 由于尚未在模拟中显式
Setup
方法,因此它将返回默认值,在这种情况下为null
。
In your second test you're calling the actual implementation of Index
because you're constructing an actual instance of the HomeController
class. 在第二个测试中,您正在调用
Index
的实际实现,因为您正在构造HomeController
类的实际实例。 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). 如果在模拟对象的实例上调用
GetType()
,则会看到它是从HomeController
派生的代理实例,该代理拦截对基类上公共定义的,可重写方法的调用(这是模拟对象的目的)。
I think that your Index method is probably virtual, which causes Moq to replace that function with a mocked function. 我认为您的Index方法可能是虚拟的,这会导致Moq用模拟函数替换该函数。 In order to prevent that, you need to set the CallBase property on the mock.
为了防止这种情况,您需要在模拟上设置CallBase属性。
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) (一个更简单的方法是创建一个可以从HttpContext提取主体的专用模型绑定程序,然后可以将主体作为输入参数传递给您的方法)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.