模拟 Asp.net-mvc 控制器上下文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32640/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 22:44:19  来源:igfitidea点击:

Mocking Asp.net-mvc Controller Context

asp.net-mvcunit-testingmockingmoqrhino-mocks

提问by Dane O'Connor

So the controller context depends on some asp.net internals. What are some ways to cleanly mock these up for unit tests? Seems like its very easy to clog up tests with tons of setup when I only need, for example, Request.HttpMethod to return "GET".

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

I've seen some examples/helpers out on the nets, but some are dated. Figured this would be a good place to keep the latest and greatest.

我在网上看到了一些例子/帮助者,但有些已经过时了。认为这将是保留最新和最好的好地方。

I'm using latest version of rhino mocks

我正在使用最新版本的犀牛模拟

采纳答案by Haacked

Using MoQ it looks something like this:

使用 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);

I think the Rhino Mocks syntax is similar.

我认为 Rhino Mocks 的语法是相似的。

回答by Maksym Kozlenko

Here is a sample unit test class using MsTest and Moq which mocks HttpRequest and HttpResponse objects. (.NET 4.0, ASP.NET MVC 3.0 )

这是一个使用 MsTest 和 Moq 的示例单元测试类,它模拟 HttpRequest 和 HttpResponse 对象。(.NET 4.0、ASP.NET MVC 3.0)

Controller action get value from request and sets http header in response objects. Other http context objects could be mocked up in similar way

控制器操作从请求中获取值并在响应对象中设置 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);
    }
}

回答by Dane O'Connor

Here's a snippet from Jason's link. Its the same as Phil's method but uses rhino.

这是杰森链接的片段。它与菲尔的方法相同,但使用犀牛。

Note: mockHttpContext.Request is stubbed to return mockRequest beforemockRequest's internals are stubbed out. I believe this order is required.

注意:mockHttpContext.RequestmockRequest 的内部被存根之前被存根以返回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
...

回答by Gabe Moothart

The procedure for this seems to have changed slightly in MVC2 (I'm using RC1). Phil Haack's solution doesn't work for me if the action requires a specific method ([HttpPost], [HttpGet]). Spelunking around in Reflector, it looks like the method for verifying these attributes has changed. MVC now checks request.Headers, request.Form, and request.QueryStringfor a X-HTTP-Method-Overridevalue.

这个过程在 MVC2 中似乎略有变化(我使用的是 RC1)。如果操作需要特定方法 ( [HttpPost], [HttpGet]) ,Phil Haack 的解决方案对我不起作用。在 Reflector 中四处探索,似乎验证这些属性的方法发生了变化。MVC 现在检查request.Headersrequest.Formrequest.QueryStringX-HTTP-Method-Override值。

If you add mocks for these properties, it works:

如果为这些属性添加模拟,它会起作用:

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);

回答by RoyOsherove

Or you can do this with Typemock Isolator with no need to send in a fake controller at all:

或者,您可以使用 Typemock Isolator 执行此操作,而根本不需要发送假控制器:

Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");

回答by RoyOsherove

i've finished with this spec

我已经完成了这个规范

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()
    {
    }
} 

and the juice is here

果汁在这里

[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));
    }
}

enjoy

请享用

回答by Matt Hinze

I find that long mocking procedure to be too much friction.

我发现冗长的模拟过程摩擦太大。

The best way we have found - using ASP.NET MVC on a real project - is to abstract the HttpContext to an IWebContext interface that simply passes through. Then you can mock the IWebContext with no pain.

我们发现的最好方法——在实际项目中使用 ASP.NET MVC——是将 HttpContext 抽象为一个简单传递的 IWebContext 接口。然后你可以毫不费力地模拟 IWebContext。

Here is an example

这是一个例子