2011-05-12 49 views
0

我寫在C#asp.net MVC3項目,我有一個助手類,看起來像這樣:靜態輔助阻止我單元測試

using System.Collections.Generic; 
using System.Web; 

namespace CoPrice.Helpers 
{ 
    public static class Messages 
    { 
     public static IList<Message> All 
     { 
      get 
      { 
       var list = ((IList<Message>)HttpContext.Current.Session["_messages"]) ?? new List<Message>(); 
       HttpContext.Current.Session["_messages"] = new List<Message>(); 
       return list; 
      } 
     } 

     public static bool Exists 
     { 
      get 
      { 
       return (((IList<Message>)HttpContext.Current.Session["_messages"]) ?? new List<Message>()).Count > 0; 
      } 
     } 

     public static void Add(MessageType type, string message) 
     { 
      Message m = new Message 
      { 
       Type = type, 
       Text = message 
      }; 
      HttpContext.Current.Session["_messages"] = HttpContext.Current.Session["_messages"] as List<Message> ?? new List<Message>(); 
      ((IList<Message>)HttpContext.Current.Session["_messages"]).Add(m); 
     } 

     public enum MessageType 
     { 
      Info, 
      Success, 
      Error 
     } 
     public struct Message 
     { 
      public MessageType Type; 
      public string Text; 
     } 
    } 
} 

然而,當我嘗試在測試中使用它們會崩潰(原因是HttpContext.Current beeing null)。我如何在測試和應用程序本身中完成這項工作?我不介意必須改變這個類來使用別的不是HttpContext.Current來訪問會話,但我希望它具有屬性,因此它不能將會話對象作爲參數。

關於如何解決這個問題的任何想法?

+0

爲什麼單身是不好的很好的例子。 – Andrey 2011-05-12 22:25:38

+0

好吧,Singeltons並不總是壞。 – Alxandr 2011-05-12 22:26:33

+0

爲什麼很難測試會話變量的很好的例子 – Jason 2011-05-12 23:01:02

回答

2

您需要定義一個IMessagesProvider並使用一個DI容器來注入IMessagesProvider的實現。在實際使用中,您將有一個使用ASP.Net會話的實現。在測試中,你大多會嘲笑它。順便說一句,你可能不應該有一個靜態的消息類。實際上,IMessagesProvider可能會替換您的靜態消息類。

例:

public class FooController : Controller 
{ 
    IMessagesProvider _messages; 

    public FooController(IMessagesProvider messages) 
    { 
     // Your DI container will inject the messages provider automatically. 
     _messages = messages; 
    } 

    public ActionResult Index() 
    { 
     ViewData["messages"] = _messages.GetMessages(Session.SessionId); 
     return View(); 
    } 
} 

你要知道,這是一個非常簡單的例子。事實上,你可能需要一個更多的類,這是驅動視圖的模型。快速介紹:

public ActionResult Index() 
{ 
    var model = PrepareModel(_messages); 
    return View(model); 
} 

「PrepareModel」是你自己的方法,該方法實例化一個新的模型類,提供必要的數據填充它,然後你把它送上你的看法。每個視圖定義一個模型類是很典型的。例如。你會有像「SignupFormModel」,「DashboardModel」,「ChatModel」等模型類,這樣做可以讓你擁有強類型的視圖(好東西)。

+0

嗯。你能提供更多的例子來說明如何做到這一點?我根本沒有使用太多的物聯網。而且,我需要能夠從我的視圖中使用這個消息提供程序(這就是爲什麼我使它成爲靜態的原因,因爲它在我的佈局文件中)。 – Alxandr 2011-05-12 22:29:51

+0

這是一個非常大的話題。我首先看看AutoFac以及如何獲取ASP.Net MVC。只要通過教程,您就會看到這個概念。此外,你的視圖應該*不*知道你的消息類,這是你的控制器的用途。控制器將編排發送到視圖的數據。 – xanadont 2011-05-12 22:34:44

+0

是的,我知道,但這意味着我需要將消息的可用性添加到EVERY控制器中,導致它被佈局視圖使用。 – Alxandr 2011-05-12 22:36:44

0

@xanadont是正確的,你需要把Messages成正常類繼承的模擬會話對象。但是(我知道這在某些圈子裏是異端的),不需要一個一次性界面和一個全面的DI框架。只是讓你的Messages類的虛方法,所以你可以嘲笑他們在你的單元測試,並使用構造器注入:

public class FooController : Controller 
{ 
    Messages _messages; 

    // MVC will call this ctor 
    public FooController() : this(new Messages()) 
    { 
    } 

    // call this ctor in your unit-tests with a mock object, testing subclass, etc. 
    public FooController(Messages messages) 
    { 
     _messages = messages; 
    }  
} 
+0

確實如此,但從長遠來看,使用DI容器可以節省更多的時間。當這個擴展到幾個控制器和多個其他需要注入的依賴項時,手動完成所有配置(以及兩種不同的方案:運行模式和測試模式)將很快變得非常繁瑣。 – xanadont 2011-05-13 14:23:21