2008-10-06 82 views
44

我讀了一些關於這裏的答案回覆:測試視圖和控制器,和嘲諷,但我仍然無法弄清楚如何測試一個ASP.NET MVC控制器,讀取和設置會話值(或任何其他基於上下文的變量) 如何爲我的測試方法提供(Session)上下文?嘲笑答案嗎?任何人都有例子嗎? 基本上,我想假會話之前我所說的控制方法,並有控制器使用該會話。有任何想法嗎?ASP/NET MVC:測試控制器與會話?懲戒?

回答

43

上僞造的控制器上下文退房斯蒂芬·瓦爾特的帖子:

ASP.NET MVC Tip #12 – Faking the Controller Context

[TestMethod] 
public void TestSessionState() 
{ 
    // Create controller 
    var controller = new HomeController(); 


    // Create fake Controller Context 
    var sessionItems = new SessionStateItemCollection(); 
    sessionItems["item1"] = "wow!"; 
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems); 
    var result = controller.TestSession() as ViewResult; 


    // Assert 
    Assert.AreEqual("wow!", result.ViewData["item1"]); 

    // Assert 
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]); 
} 
12

ASP.NET MVC框架是不是很假友好(或者更確切地說,需要太多的設置適當地戲弄,導致測試時,恕我直言,摩擦力太大),由於它的使用抽象基類,而不是接口。我們已經爲每個請求和基於會話的存儲編寫了抽象概念。我們保持這些抽象非常輕鬆,然後我們的控制器依賴於每個請求或每個會話存儲的抽象。

例如,這裏是我們如何管理窗體身份驗證的東西。我們有一個ISecurityContext:

public interface ISecurityContext 
{ 
    bool IsAuthenticated { get; } 
    IIdentity CurrentIdentity { get; } 
    IPrincipal CurrentUser { get; set; } 
} 

有了這樣一個具體的實現:

public class SecurityContext : ISecurityContext 
{ 
    private readonly HttpContext _context; 

    public SecurityContext() 
    { 
     _context = HttpContext.Current; 
    } 

    public bool IsAuthenticated 
    { 
     get { return _context.Request.IsAuthenticated; } 
    } 

    public IIdentity CurrentIdentity 
    { 
     get { return _context.User.Identity; } 
    } 

    public IPrincipal CurrentUser 
    { 
     get { return _context.User; } 
     set { _context.User = value; } 
    } 
} 
2

斯科特Hanselman有一個關於用MVC如何create a file upload quickapp後,討論moking和專門解決「如何嘲笑的事情,AREN '嘲笑友好。「

+0

Hanselman的救援...... – Codewerks 2008-10-07 01:37:04

5

我發現嘲諷是相當容易的。這是一個使用moq模擬httpContextbase(包含請求,會話和響應對象)的示例。

[TestMethod] 
     public void HowTo_CheckSession_With_TennisApp() { 
      var request = new Mock<HttpRequestBase>(); 
      request.Expect(r => r.HttpMethod).Returns("GET");  

      var httpContext = new Mock<HttpContextBase>(); 
      var session = new Mock<HttpSessionStateBase>(); 

      httpContext.Expect(c => c.Request).Returns(request.Object); 
      httpContext.Expect(c => c.Session).Returns(session.Object); 

      session.Expect(c => c.Add("test", "something here"));    

      var playerController = new NewPlayerSignupController(); 
      memberController.ControllerContext = new ControllerContext(new RequestContext(httpContext.Object, new RouteData()), playerController);   

      session.VerifyAll(); // function is trying to add the desired item to the session in the constructor 
      //TODO: Add Assertions 
     } 

希望有所幫助。

+2

哇,這是一個很大的工作,只是爲了嘲笑一個方法:)這顯然是「太多的設置「氣味,這是使用抽象基類而不是接口作爲依賴關係的結果。 – chadmyers 2008-10-06 23:18:49

+1

當然,我曾經見過他們把設置的代碼放在一個「助手」類,並一遍又一遍地用它的幾個項目。 – Korbin 2008-10-07 22:24:51

8

隨着MVC RC 1的ControllerContext封裝的HttpContext和公開它作爲屬性。這使嘲笑變得更容易。要用Moq模擬會話變量,請執行以下操作:

var controller = new HomeController(); 
var context = MockRepository.GenerateStub<ControllerContext>(); 
context.Expect(x => x.HttpContext.Session["MyKey"]).Return("MyValue"); 
controller.ControllerContext = context; 

有關更多詳細信息,請參閱Scott Gu's post

0

由於HttpContext是靜態的,我使用Typemock Isolator來模擬它,Typemock也有一個爲ASP.NET unit testing構建的Add-in自定義,名爲Ivonna

2

我用以下解決方案 - 製作一個控制器,我的所有其他控制器繼承。

public class TestableController : Controller 
{ 

    public new HttpSessionStateBase Session 
    { 
     get 
     { 
      if (session == null) 
      { 
       session = base.Session ?? new CustomSession(); 
      } 
      return session; 
     } 
    } 
    private HttpSessionStateBase session; 

    public class CustomSession : HttpSessionStateBase 
    { 

     private readonly Dictionary<string, object> dictionary; 

     public CustomSession() 
     { 
      dictionary = new Dictionary<string, object>(); 
     } 

     public override object this[string name] 
     { 
      get 
      { 
       if (dictionary.ContainsKey(name)) 
       { 
        return dictionary[name]; 
       } else 
       { 
        return null; 
       } 
      } 
      set 
      { 
       if (!dictionary.ContainsKey(name)) 
       { 
        dictionary.Add(name, value); 
       } 
       else 
       { 
        dictionary[name] = value; 
       } 
      } 
     } 

     //TODO: implement other methods here as needed to forefil the needs of the Session object. the above implementation was fine for my needs. 

    } 

} 

然後使用代碼如下:

public class MyController : TestableController { }