【问题标题】:Mocking Asp.net-mvc Controller Context模拟 Asp.net-mvc 控制器上下文
【发布时间】:2010-09-07 03:41:12
【问题描述】:

所以控制器上下文依赖于一些 asp.net 内部。有哪些方法可以干净地模拟这些以进行单元测试?当我只需要例如 Request.HttpMethod 返回“GET”时,似乎很容易用大量设置阻塞测试。

我在网上看到了一些示例/帮助程序,但有些已经过时了。认为这是保持最新和最伟大的好地方。

我正在使用最新版本的 rhino mocks

【问题讨论】:

  • 我正在考虑这样做。但只需要数据库连接的模拟。我没有测试数据库映射,而是将函数移到了一个常规类中,只测试了那个函数,没有数据库连接。

标签: asp.net-mvc unit-testing mocking moq 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.Headersrequest.Formrequest.QueryStringX-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().Object);否则我会得到一个 nullreferenceexception
    【解决方案4】:

    或者您可以使用 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。 stackoverflow.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

        【讨论】:

        • 你能再解释一下吗?
        猜你喜欢
        • 1970-01-01
        • 2016-09-22
        • 1970-01-01
        • 2010-11-01
        • 2010-12-26
        • 1970-01-01
        • 2015-02-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多