asp.net mvc如何正确测试控制器

时间:2010-06-18 10:25:03

标签: asp.net-mvc unit-testing mocking

我在测试控制器时遇到了困难。原来我的测试控制器看起来像这样:

SomethingController CreateSomethingController()
{
    var somethingData = FakeSomethingData.CreateFakeData();
    var fakeRepository = FakeRepository.Create();

    var controller = new SomethingController(fakeRepository);

    return controller;
}

在我获得Request.IsAjaxRequest()部分代码之前,这适用于大多数测试。所以我不得不模拟HttpContext和HttpRequestBase。所以我的代码改为看起来像:

public class FakeHttpContext : HttpContextBase
{
    bool _isAjaxRequest;

    public FakeHttpContext( bool isAjaxRequest = false )
    {
        _isAjaxRequest = isAjaxRequest;
    }

    public override HttpRequestBase Request
    {
        get
        {
            string ajaxRequestHeader = "";

            if ( _isAjaxRequest )
                ajaxRequestHeader = "XMLHttpRequest";

            var request = new Mock<HttpRequestBase>();
            request.SetupGet( x => x.Headers ).Returns( new WebHeaderCollection 
            {
                {"X-Requested-With", ajaxRequestHeader} 
            } );

            request.SetupGet( x => x["X-Requested-With"] ).Returns( ajaxRequestHeader );

            return request.Object;
        }
    }

    private IPrincipal _user;

    public override IPrincipal User
    {
        get
        {
            if ( _user == null )
            {
                _user = new FakePrincipal();
            }
            return _user;
        }
        set
        {
            _user = value;
        }
    }
}


SomethingController CreateSomethingController()
{
    var somethingData = FakeSomethingData.CreateFakeData();
    var fakeRepository = FakeRepository.Create();

    var controller = new SomethingController(fakeRepository);

    ControllerContext controllerContext = new ControllerContext( new FakeHttpContext( isAjaxRequest ), new RouteData(), controller );
     controller.ControllerContext = controllerContext;

    return controller;
}

现在它已进入我的控制器中的那个阶段,我调用Url.Route并且Url为空。所以看起来我需要开始为我的控制器模拟路线。

我似乎花了更多的时间在Google上搜索伪造/模拟对象,然后调试以确保我的假货是正确的,而不是实际编写测试代码。有没有更简单的方法来测试控制器?我看过MvcContrib的TestControllerBuilder,它可以解决一些问题,但似乎并没有做任何事情。还有什么可以做的工作,让我专注于编写测试而不是写嘲笑?

由于

4 个答案:

答案 0 :(得分:1)

您可以使用一些开箱即用的库,其中包含一些这些对象。例如RhinoMockNMock ...等我个人使用Moq - 它足够好且免费。在Moq中我最喜欢的是linq表达式。

答案 1 :(得分:1)

大多数模拟引擎都会为你做这一切。我使用RhinoMocks,但还有更多可用。另外Moles是一个非常新颖而有趣的模拟引擎(这通常伴随着Pex,你的单位测试武器库中的弹药更多)

答案 2 :(得分:1)

MvcContrib + RhinoMocks。查看MvcContrib.TestHelper库中的TestControllerBuilder。这是官方的文章:http://mvccontrib.codeplex.com/wikipage?title=TestHelper#Examples

以下是模拟控制器以测试UrlHelper的示例:ASP.NET MVC: Mock controller.Url.Action

以下是如何使用TestControllerBuilder的简短说明:http://codebetter.com/blogs/kyle.baley/archive/2008/03/19/testcontrollerbuilder-in-mvccontrib.aspx

答案 3 :(得分:1)

您可以将IAjaxRequest传递给构造函数,而不是模拟东西。或者使它成为基础构造函数类属性(并使用属性注入)。或者,您可以使构造函数实现IAjaxRequest,然后对将设置IAjaxRequest的基础构造函数类应用全局操作过滤器。

这将有助于抽象很多东西,包括HttpContext的东西。只是不要抽象IHttpContext,抽象IUserContext,ISessionStorage,IAuthentication,IRequestDetails ......

另一种方法是直接在需要特定信息的方法上使用模型绑定器。例如,请参阅this post。您可以创建将为您提供IsAjaxRequest的活页夹,然后您只需动作即可接受此参数。工作得非常好,因为信息完全按照需要它的方法提供,而不是整个控制器。