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

发布于 2024-12-12 03:45:15 字数 793 浏览 0 评论 0原文

在我的简单 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

萌吟 2024-12-19 03:45:15

您的单元测试中有一些奇怪的事情。您正在对控制器进行单元测试,但正在模拟被测对象的创建: var homeController = Mock(..); 这是不正确的。

以下是将模拟用户注入到您愿意进行单元测试的控制器中的正确方法:

// 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
...

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
...
薄荷→糖丶微凉 2024-12-19 03:45:15

我认为你应该模拟 HttpContext 供控制器使用。 我提供了一个您可以使用的另一个答案。作为 Steve Rowbotham 说,你应该模拟被测系统的依赖关系(即控制器的依赖关系),而不是模拟被测系统本身;您想要测试控制器的真实版本而不是模拟版本:)

使用链接中的 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); 

您可以更进一步,创建 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 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.

永不分离 2024-12-19 03:45:15

老实说,这看起来是一个非常奇怪的测试,因为您正在模拟被测系统 (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 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).

空名 2024-12-19 03:45:15

我认为您的 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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文