2009-08-04 111 views
21

我有一個單元測試裝置,我試圖在ASP.NET MVC控制器上測試ControllerAction,這個控制器用於Web應用程序的成員函數。我試圖嘲笑測試的HttpContext。被測試的ControllerAction實際上在HttpContext上設置了屬性,例如Session值,Response.Cookies值等。這不是所有的代碼,但這裏是我試圖運行的測試的粗略示例:用Moq嘲弄HttpContextBase

[Test] 
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser() 
{ 
    var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock}; 
    context.SetupAllProperties(); 
    var provider = new Mock<MembershipProvider>(new object[] {context.Object}); 
    var controller = new AccountController(context.Object, provider.Object); 
    // This just sets up a local FormCollection object with valid user data 
    // in it to use to attempt the registration 
    InitializeValidFormData(); 
    ActionResult result = controller.Register(_registrationData); 
    Assert.IsInstanceOfType(typeof(ViewResult), result); 
    // Here is where I'd like to attempt to do Assertions against properties 
    // of the HttpContext, like ensuring that a Session object called "User" 
    // exists, and new auth cookie exists on the Response.Cookies collection. 
    // So far I've been unable to successfully check the values of those properties. 
    // I've been unsuccessful in getting those properties setup correctly on my 
    // mock object so that my ControllerAction can actually *set* their values, 
    // and that I can make assertions on them afterwards. The above code actually 
    // generates a StackOverflowException (which I've reported) on the 
    // context.SetupAllProperties() call. What am I doing wrong, or what do I need 
    // to do to be able to set and assert on those context properties? 
} 

不知道我做錯了,但我喜歡它,如果有人能在正確的方向指向我,告訴我如何設置這個模擬HttpContextBase對象,使得我的控制器可實際設定的值在其屬性上,我可以對這些屬性做出斷言,以確保我的ControllerAction正在做我所需要的。

我接近這個錯誤的方式嗎?我知道MVC控制器有一個ControllerContext,我可以用它來爲Session等設置值,但是我無法弄清楚如何在沒有注入的情況下模擬這些東西。是否有某種方式來代替? (我也需要能夠將上下文傳遞給我的MembershipProvider)這是否會是一個更好的方法?

謝謝。

回答

31

我使用的代碼包括在他的Pro Asp.NET MVC book中的一些代碼Steve Sanderson的版本......我目前有一個道德困境,無論是在這裏發佈代碼是否可以。我如何妥協一個高度精簡的版本? ;)

因此,這可以很容易地重用,創建一個類似於下面的類,你會通過你的控制器。這將設置你的嘲弄,並將其設置爲您控制器的ControllerContext

public class ContextMocks 
{ 
    public Moq.Mock<HttpContextBase> HttpContext { get; set; } 
    public Moq.Mock<HttpRequestBase> Request { get; set; } 
    public RouteData RouteData { get; set; } 

    public ContextMocks(Controller controller) 
    { 
     //define context objects 
     HttpContext = new Moq.Mock<HttpContextBase>(); 
     HttpContext.Setup(x => x.Request).Returns(Request.Object); 
     //you would setup Response, Session, etc similarly with either mocks or fakes 

     //apply context to controller 
     RequestContext rc = new RequestContext(HttpContext.Object, new RouteData()); 
     controller.ControllerContext = new ControllerContext(rc, controller); 
    } 
} 

然後在您的測試方法,你只是創建ContextMocks的實例,並通過在控制器對象,你正在測試:

[Test] 
Public void test() 
{ 
    var mocks = new ContextMocks(controller); 
    var req = controller.Request; 
    //do some asserts on Request object 
} 

似乎與克雷格的例子非常相似,但這是與Moq v3。我必須爲Steve Sanderson提供道具 - 我將它用作測試各種傳統上難以測試的東西的基礎:餅乾,會話,請求方法,查詢字符串等等。

22

Here's how I do it

public static HttpContextBase FakeHttpContext() 
    { 
     var context = new Mock<HttpContextBase>(); 
     var request = new Mock<HttpRequestBase>(); 
     var response = new Mock<HttpResponseBase>(); 
     var session = new Mock<HttpSessionStateBase>(); 
     var server = new Mock<HttpServerUtilityBase>(); 
     var user = new Mock<IPrincipal>(); 
     var identity = new Mock<IIdentity>(); 

     request.Expect(req => req.ApplicationPath).Returns("~/"); 
     request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/"); 
     request.Expect(req => req.PathInfo).Returns(string.Empty); 
     response.Expect(res => res.ApplyAppPathModifier(It.IsAny<string>())) 
      .Returns((string virtualPath) => virtualPath); 
     user.Expect(usr => usr.Identity).Returns(identity.Object); 
     identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true); 

     context.Expect(ctx => ctx.Request).Returns(request.Object); 
     context.Expect(ctx => ctx.Response).Returns(response.Object); 
     context.Expect(ctx => ctx.Session).Returns(session.Object); 
     context.Expect(ctx => ctx.Server).Returns(server.Object); 
     context.Expect(ctx => ctx.User).Returns(user.Object); 

     return context.Object; 
    } 

這是the MvcMockHelpers library released by Scott Hanselman的增強版本。這是Moq 2.0代碼;在語法上略有不同3.

+0

是的,謝謝,但我使用Moq 3.1.416.3,所以它確實有助於查看該版本的語法。主要是因爲,顯然,如果我理解正確,「設置」屬性在3.x中有很大的不同。感謝這個例子。 – 2009-08-04 15:48:02

+6

用安裝替換Expect,它應該按原樣工作。 – 2009-08-04 16:21:35