在我简单的Index()
ActionMethod中,我引用了User.Identity属性。所以,我认为我需要嘲笑它。
所以我创建了一个模拟HomeController
并在我的单元测试中使用它。当我这样做时,ActionMethod
返回null作为视图。当我用一个具体的实例替换mock'd控制器时(当然注释掉对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!
为什么会这样?
答案 0 :(得分:3)
你的单元测试中有一些奇怪的东西。您正在对一个控制器进行单元测试,但您正在模拟被测对象的创建:var homeController = Mock<HomeController>(..);
这是不正确的。
以下是将模拟用户注入您愿意进行单元测试的控制器的正确方法:
// 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
...
答案 1 :(得分:2)
我认为你应该嘲笑一个HttpContext供控制器使用。 I provided one on another answer that you could use。正如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方法,以使用模拟上下文设置控制器。
答案 2 :(得分:1)
说实话,这看起来像是一个非常奇怪的测试,因为你在嘲笑被测系统(SUT),换句话说是HomeController
。通常可以模拟SUT的依赖关系,设置对mock的期望并将模拟注入SUT以确认它与其依赖项正确交互。
当您创建HomeController
的模拟时,Moq正在创建一个继承自HomeController的类并覆盖虚拟方法Index
。因此,当您在模拟上调用Index
时,您不会调用Index
类中已定义的HomeController
的实现,而是覆盖已被覆盖的类Setup
。由于您没有在模拟上明确null
方法,因此它将返回默认值,在本例中为Index
。
在您的第二个测试中,您正在调用HomeController
的实际实现,因为您正在构建GetType()
类的实际实例。如果在模拟对象的实例上调用HomeController
,那么您将看到它是从{{1}}派生的代理实例,它拦截对基类上公开定义的可覆盖方法的调用(这是模拟对象的很大一部分目的。)
答案 3 :(得分:1)
我认为您的Index方法可能是虚方法,这会导致Moq用模拟函数替换该函数。为了防止这种情况,您需要在模拟上设置CallBase属性。
但是,我同意其他回复,你不应该嘲笑你的控制器,但你应该嘲笑你的依赖。
(更简单的方法是创建一个专门的模型绑定器,可以从HttpContext中获取主体,然后你可以将主体作为输入参数传递给你的方法)