模拟 Asp.net-mvc 控制器上下文
Posted
技术标签:
【中文标题】模拟 Asp.net-mvc 控制器上下文【英文标题】:Mocking Asp.net-mvc Controller Context 【发布时间】:2010-09-07 03:41:12 【问题描述】:所以控制器上下文依赖于一些 asp.net 内部。有哪些方法可以干净地模拟这些以进行单元测试?当我只需要例如 Request.HttpMethod 返回“GET”时,似乎很容易用大量设置阻塞测试。
我在网上看到了一些示例/帮助程序,但有些已经过时了。认为这是保持最新和最伟大的好地方。
我正在使用最新版本的 rhino mocks
【问题讨论】:
我正在考虑这样做。但只需要数据库连接的模拟。我没有测试数据库映射,而是将函数移到了一个常规类中,只测试了那个函数,没有数据库连接。 【参考方案1】:这是一个使用 MsTest 和 Moq 模拟 HttpRequest 和 HttpResponse 对象的示例单元测试类。 (.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);
【讨论】:
非常非常感谢!这对我帮助很大。我希望我能投票十次。【参考方案2】:我已经完成了这个规范
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));
享受
【讨论】:
【参考方案3】:这个过程在 MVC2 中似乎略有改变(我使用的是 RC1)。如果操作需要特定方法([HttpPost]
、[HttpGet]
),Phil Haack 的解决方案对我不起作用。在 Reflector 中探索,看起来验证这些属性的方法已经改变。 MVC 现在检查 request.Headers
、request.Form
和 request.QueryString
的 X-HTTP-Method-Override
值。
如果您为这些属性添加模拟,它会起作用:
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);
【讨论】:
这对我有用,但是在 MVC2 RC 中我还必须添加以下内容: request.Setup(r => r.Files).Returns(new Mock或者您可以使用 Typemock Isolator 来做到这一点,而根本不需要发送假控制器:
Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");
【讨论】:
【参考方案5】:使用 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 的语法是相似的。
【讨论】:
这在 MVC3 中不再有效。当在 RouteData 上调用非虚拟、不可模拟的方法 GetRequiredString 时,传入一个空的 RouteData 将引发异常。 @ScottKoon 请提供它应该是什么样子的演示 这个问题的答案向您展示了如何模拟 RouteData。 ***.com/questions/986183/…【参考方案6】:这是来自 Jason 链接的 sn-p。它与菲尔的方法相同,但使用犀牛。
注意:mockHttpContext.Request 被存根以返回 mockRequest在 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
...
【讨论】:
【参考方案7】:我发现冗长的模拟过程太麻烦了。
我们发现的最好的方法——在实际项目中使用 ASP.NET MVC——是将 HttpContext 抽象为一个简单地通过的 IWebContext 接口。然后,您可以轻松地模拟 IWebContext。
这是example
【讨论】:
你能再解释一下吗?以上是关于模拟 Asp.net-mvc 控制器上下文的主要内容,如果未能解决你的问题,请参考以下文章
剑道 DropDownListFor() 与 ASP.NET-MVC
在 asp.net-mvc 中从服务器读取文本文件的最佳方法是啥
如何将消息从我的存储库类返回到我的控制器,然后返回到我在 asp.net-mvc 中的视图?