简体   繁体   English

当我模拟ASP.NET MVC控制器时,我的ActionMethod不返回任何视图。 为什么?

[英]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. 为了防止这种情况,您需要在模拟上设置Call​​Base属性。

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.

相关问题 MVC ASP.NET提交给Controller时在View中获取ViewData.Model - MVC ASP.NET Getting back my ViewData.Model in my View when I submit to a Controller 为什么在调用视图时,视图中的嵌套对象为null。 ASP.Net MVC - Why nested objects in view are null when calling the view. ASP.Net MVC 如何将对象从我的视图传递到 ASP.NET MVC 中的控制器? - How can I pass an object from my view to my controller in ASP.NET MVC? 当我从具有多个 forms 的视图提交表单时,为什么在我的 controller 中收到 null 值? 带 ASP.NET 内核 5 MVC - Why do I receive null values in my controller when I submit a form from a view with multiple forms? With ASP.NET Core 5 MVC 为什么我的 model 中的这个值在我的 controller 中使用时返回 null? (ASP.NET MVC) - Why does this value in my model return null when used in my controller? (ASP.NET MVC) 为什么我的ASP.NET MVC模型填充在控制器中,但视图中为Null? - Why Is My ASP.NET MVC Model Filled in the Controller, but Null in the View? 当我在asp.net mvc控制器操作中验证失败时,如何保留我的URL - how can i keep my url when my validation fail in asp.net mvc controller action Asp.net MVC 4 razor如何在视图中保存列表时如何在控制器中保留类型 - Asp.net mvc 4 razor How keep types in my Controller when saving a list in a view 如何在ASP.NET MVC 5项目中获得“添加控制器”和“添加视图”菜单选项? - How do I get the “Add Controller” and “Add View” menu options in my ASP.NET MVC 5 project? ASP.NET MVC5。 我不知道为什么我的表单返回空的viewmodel对象 - ASP.NET MVC5. I don't know why my form returns null viewmodel object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM