嘲笑Asp.net-mvc控制器上下文
所以控制器上下文取决于一些asp.net内部。 有什么方法可以干净地模拟这些unit testing? 似乎很容易阻塞testing吨设置时,我只需要,例如,Request.HttpMethod返回“GET”。
我在网上看过一些例子/帮手,但有些是过时的。 认为这将是保持最新和最好的一个好地方。
我正在使用最新版本的犀牛嘲笑
使用MoQ它看起来像这样:
var request = new Mock<HttpRequestBase>(); request.Expect(r => r.HttpMethod).Returns("GET"); var mockHttpContext = new Mock<HttpContextBase>(); mockHttpContext.Expect(c => c.Request).Returns(request.Object); var controllerContext = new ControllerContext(mockHttpContext.Object , new RouteData(), new Mock<ControllerBase>().Object);
我认为Rhino Mocks的语法是相似的。
这里是一个使用MsTest和Moq模拟HttpRequest和HttpResponse对象的示例unit testing类。 (.NET 4.0,ASP.NET MVC 3.0)
控制器操作从请求中获取值,并在响应对象中设置http头。 其他http上下文对象也可以用类似的方法来模拟
[TestClass] public class MyControllerTest { protected Mock<HttpContextBase> HttpContextBaseMock; protected Mock<HttpRequestBase> HttpRequestMock; protected Mock<HttpResponseBase> HttpResponseMock; [TestInitialize] public void TestInitialize() { HttpContextBaseMock = new Mock<HttpContextBase>(); HttpRequestMock = new Mock<HttpRequestBase>(); HttpResponseMock = new Mock<HttpResponseBase>(); HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object); HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object); } protected MyController SetupController() { var routes = new RouteCollection(); var controller = new MyController(); controller.ControllerContext = new ControllerContext(HttpContextBaseMock.Object, new RouteData(), controller); controller.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, new RouteData()), routes); return controller; } [TestMethod] public void IndexTest() { HttpRequestMock.Setup(x => x["x"]).Returns("1"); HttpResponseMock.Setup(x => x.AddHeader("name", "value")); var controller = SetupController(); var result = controller.Index(); Assert.AreEqual("1", result.Content); HttpRequestMock.VerifyAll(); HttpResponseMock.VerifyAll(); } } public class MyController : Controller { public ContentResult Index() { var x = Request["x"]; Response.AddHeader("name", "value"); return Content(x); } }
这是Jason的链接的一个片段。 它和菲尔的方法一样,但使用犀牛。
注意: 在 mockRequest的内部被删除之前, mockHttpContext.Request被stubbed返回mockRequest。 我相信这个命令是必需的。
// create a fake web context var mockHttpContext = MockRepository.GenerateMock<HttpContextBase>(); var mockRequest = MockRepository.GenerateMock<HttpRequestBase>(); mockHttpContext.Stub(x => x.Request).Return(mockRequest); // tell the mock to return "GET" when HttpMethod is called mockRequest.Stub(x => x.HttpMethod).Return("GET"); var controller = new AccountController(); // assign the fake context var context = new ControllerContext(mockHttpContext, new RouteData(), controller); controller.ControllerContext = context; // act ...
这个过程似乎已经在MVC2中稍微改变了(我正在使用RC1)。 如果操作需要特定方法( [HttpPost]
, [HttpGet]
),Phil Haack的解决scheme对我[HttpGet]
。 在Reflector中探索,看起来validation这些属性的方法已经改变了。 MVC现在检查request.Headers
, request.Form
和request.QueryString
以获取X-HTTP-Method-Override
值。
如果你为这些属性添加mock,它将起作用:
var request = new Mock<HttpRequestBase>(); request.Setup(r => r.HttpMethod).Returns("POST"); request.Setup(r => r.Headers).Returns(new NameValueCollection()); request.Setup(r => r.Form).Returns(new NameValueCollection()); request.Setup(r => r.QueryString).Returns(new NameValueCollection()); var mockHttpContext = new Mock<HttpContextBase>(); mockHttpContext.Expect(c => c.Request).Returns(request.Object); var controllerContext = new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object);
或者你可以用Typemock Isolator来做到这一点,不需要发送假控制器:
Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");
我完成了这个规范
public abstract class Specification <C> where C: Controller { protected C controller; HttpContextBase mockHttpContext; HttpRequestBase mockRequest; protected Exception ExceptionThrown { get; private set; } [SetUp] public void Setup() { mockHttpContext = MockRepository.GenerateMock<HttpContextBase>(); mockRequest = MockRepository.GenerateMock<HttpRequestBase>(); mockHttpContext.Stub(x => x.Request).Return(mockRequest); mockRequest.Stub(x => x.HttpMethod).Return("GET"); EstablishContext(); SetHttpContext(); try { When(); } catch (Exception exc) { ExceptionThrown = exc; } } protected void SetHttpContext() { var context = new ControllerContext(mockHttpContext, new RouteData(), controller); controller.ControllerContext = context; } protected T Mock<T>() where T: class { return MockRepository.GenerateMock<T>(); } protected abstract void EstablishContext(); protected abstract void When(); [TearDown] public virtual void TearDown() { } }
果汁在这里
[TestFixture] public class When_invoking_ManageUsersControllers_Update :Specification <ManageUsersController> { private IUserRepository userRepository; FormCollection form; ActionResult result; User retUser; protected override void EstablishContext() { userRepository = Mock<IUserRepository>(); controller = new ManageUsersController(userRepository); retUser = new User(); userRepository.Expect(x => x.GetById(5)).Return(retUser); userRepository.Expect(x => x.Update(retUser)); form = new FormCollection(); form["IdUser"] = 5.ToString(); form["Name"] = 5.ToString(); form["Surename"] = 5.ToString(); form["Login"] = 5.ToString(); form["Password"] = 5.ToString(); } protected override void When() { result = controller.Edit(5, form); } [Test] public void is_retrieved_before_update_original_user() { userRepository.AssertWasCalled(x => x.GetById(5)); userRepository.AssertWasCalled(x => x.Update(retUser)); } }
请享用
我发现那个漫长的嘲弄程序太多了。
我们发现的最好的方法是在一个真实的项目中使用ASP.NET MVC,将HttpContext抽象为简单地通过的IWebContext接口。 那么你可以毫不费力地嘲笑IWebContext。
这是一个例子