当我模拟 ASP.NET MVC 控制器时,我的 ActionMethod 不返回任何视图。为什么?
在我的简单 Index()
ActionMethod 中,我引用了 User.Identity 属性。所以,我想我需要嘲笑这一点。
所以我创建了一些模拟 HomeController
并在我的单元测试中使用它。当我这样做时,ActionMethod
返回 null 作为视图。当我用具体实例替换模拟控制器时(当然还要注释掉对 User.Identity 的任何引用),则会返回正确的视图。
例如。
// 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.
但是当我这样做时(并注释掉任何 User
引用),它起作用了......
// Arrange.
var homeController = new HomeController(..);
// Act.
var result = homeController.Index();
// Assert.
Assert.IsNotNull(result); // Tick!
有什么想法吗?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的单元测试中有一些奇怪的事情。您正在对控制器进行单元测试,但正在模拟被测对象的创建:
var homeController = Mock(..);
这是不正确的。以下是将模拟用户注入到您愿意进行单元测试的控制器中的正确方法:
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:
我认为你应该模拟 HttpContext 供控制器使用。 我提供了一个您可以使用的另一个答案。作为 Steve Rowbotham 说,你应该模拟被测系统的依赖关系(即控制器的依赖关系),而不是模拟被测系统本身;您想要测试控制器的真实版本而不是模拟版本:)
使用链接中的 HttpContextBase 类,您只需在测试中执行以下操作
您可以更进一步,创建 Setup 和 TearDown使用模拟上下文设置控制器的方法。
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 testYou may go a step further and create Setup and TearDown methods to set the controller up with a mocked context.
老实说,这看起来是一个非常奇怪的测试,因为您正在模拟被测系统 (SUT),即
HomeController
。通常,人们会模拟 SUT 的依赖项,设置对模拟的期望,并将模拟注入到 SUT 中,以确认它与其依赖项正确交互。当您创建
HomeController
的模拟时,Moq 将创建一个继承自 HomeController 的类并覆盖虚拟方法Index
。因此,当您在模拟上调用Index
时,您并不是调用在HomeController
类中定义的Index
的实现,而是调用被重写的实现。由于您没有显式Setup
模拟上的方法,它将返回默认值,在本例中为null
。在第二个测试中,您将调用
Index
的实际实现,因为您正在构造HomeController
类的实际实例。如果您在模拟对象的实例上调用GetType()
,那么您将看到它是派生自HomeController
的代理实例,它拦截对公共定义的调用,基类上的可重写方法(这是模拟对象的很大一部分目的)。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 methodIndex
. So when you callIndex
on the mock you're not calling the implementation ofIndex
you've defined in theHomeController
class but the overridden one. Since you've not explicitlySetup
the method on the mock it will return the default value, in this casenull
.In your second test you're calling the actual implementation of
Index
because you're constructing an actual instance of theHomeController
class. If you callGetType()
on the instance of the mock object then you'll see that it's an instance of a proxy that derives fromHomeController
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).我认为您的 Index 方法可能是虚拟的,这会导致 Moq 用模拟函数替换该函数。为了防止这种情况,您需要在模拟上设置 CallBase 属性。
但是,我同意其他回复,即您不应该嘲笑您的控制器,但您应该嘲笑您的依赖关系。
(一个更简单的方法是创建一个专门的模型绑定器,它可以从 HttpContext 获取主体,然后您可以将主体作为输入参数传递给您的方法)
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)