2013-08-04 194 views
2

我有一個服務層,它有一系列的方法。這些方法已經實現緩存,如下所示:如何對使用緩存的服務進行單元測試?

string key = "GetCategories"; 
if (CacheHandler.IsCachingEnabled() && !CacheHandler.ContainsKey(key)) 
{ 
    var categories = RequestHelper.MakeRequest("get_category_index")["categories"]; 
    var converted = categories.ToObject<List<Category>>(); 
    CacheHandler.InsertToCache(key,converted); 
    return converted; 
} 
return CacheHandler.GetCache(key) as List<Category>; 

現在,問題是我也要打個單元測試,如下所示:

[TestMethod] 
public void GetCategories() 
{ 
    IContentService contentService = new ContentService(); 
    var resp = contentService.GetCategories(); 
    Assert.IsNotNull(resp,"Should not be null"); 
} 

問題是,該HttpContext.Current在我的CacheHandler在單元測試期間顯然是空的。

解決此問題的最簡單方法是什麼?

回答

3

這個尖叫dependency injection。我認爲主要的問題是,你訪問CacheHandler靜態,所以在一個單元測試,您可以:
一)不能沒有「測試」的CacheHandler以及
二)不能提供任何其他CacheHandler到服務測試服務,例如mocked一個

如果你的情況是可能的,我想無論是重構或者至少包裹CacheHandler,使該服務訪問它的一個實例。在單元測試中,您可以使用「假」CacheHandler提供服務,該服務不會訪問HttpContext,並且可以讓您對測試本身進行非常好的控制(例如,您可以測試緩存項目時發生的情況與當它不是兩個完全獨立的單元測試)

對於嘲諷的一部分,我想這是最簡單的創建一個接口,然後使用專門用於測試一些automocking /代理生成框架,例如Rhino Mocks(但還有更多,只是發生了,我正在使用這一個,我很高興:))。另一種方法(對於初學者更容易,但在實際開發中更麻煩)將僅僅是設計CacheHandler(或其包裝),以便您可以繼承它並自行覆蓋行爲。

最後對於注入本身,我發現了一個方便的「模式」,它利用了C#默認方法參數和標準構造函數注入。該服務的構造看起來像:

public ContentService(ICacheHandler cacheHandler = null) 
{ 
    // Suppose I have a field of type ICacheHandler to store the handler 
    _cacheHandler = cacheHandler ?? new CacheHandler(...); 
} 

所以在應用程序本身,我可以調用不帶參數的構造函數(或讓框架構建的服務,如果它的ASP.NET處理程序,WCF服務或一些其他類型的類)和單元測試中,我可以提供實現上述接口的任何內容。

在犀牛嘲笑的情況下,它可以是這樣的:

var mockCacheHandler = MockRepository.GenerateMock<ICacheHandler>(); 
// Here I can mock/stub methods and properties, set expectations etc... 
var sut = new ContentService(mockCacheHandler); 
+1

它的工作!完善!這真的幫助我馬上:)犀牛嘲笑也非常好。謝謝 –

1

作爲最佳實踐(因爲我沒有做了很多以前的單元測試請儘可能具體),你只需要在測試期間,測試一件事,你所描述的有什麼多個步驟。因此,最好構建你的「RequestHelper.MakeRequest」和其他例程的測試,以便它們與緩存場景分開運行測試。單獨測試它們會讓你知道它們是否是這些例程或緩存中的問題。您可以稍後將其整合,以便將其作爲一個組進行測試。

要單獨測試緩存,可以創建一個模擬對象,以使用您需要的屬性創建HttpContext。下面是一些以前的答案,應該幫助你把這個在一起:

How do I mock the HttpContext in ASP.NET MVC using Moq?

Mock HttpContext.Current in Test Init Method

2

獨立緩存到自己的類,就像是一個代理。

public interface IContentService 
{ 
    Categories GetCategories(); 
} 

public class CachingContentSerice : IContentService 
{ 
    private readonly IContentService _inner; 

    public CachingContentSerice(IContentService _inner) 
    { 
     _inner = inner; 
    } 

    public Categories GetCategories() 
    { 
     string key = "GetCategories"; 
     if (!CacheHandler.ContainsKey(key)) 
     { 
      Catogories categories = _inner.GetCategories(); 
      CacheHandler.InsertToCache(key, categories); 
     } 
     return CacheHandler.GetCache(key); 
    } 
} 

public class ContentSerice : IContentService 
{ 
    public Categories GetCategories() 
    { 
     return RequestHelper.MakeRequest("get_category_index")["categories"]; 
    } 
} 

要啓用緩存,與緩存裝點真正ContentService

var service = new CachingContentService(new ContentService()); 

爲了測試緩存,與測試雙象構造器參數創建CachingContentService。使用test double驗證緩存:調用一次,它應該調用後面的服務。調用它兩次,它不應該調用後面的服務。

3
在洪扎Brestan的答案建議

依賴注入無疑是一個有效的解決方案,也許最好的解決方案 - 特別是如果你可能想使用在未來的ASP.NET緩存以外的東西。

但是我應該指出,您可以使用ASP.NET緩存而不需要HttpContext。您可以使用靜態屬性HttpRuntime.Cache,而不是將其引用爲HttpContext.Current.Cache

這將使您能夠在HTTP請求的上下文之外使用緩存,例如在單元測試或後臺工作線程中。事實上,我通常會建議在業務層中使用HttpRuntime.Cache進行數據緩存,以避免依賴HttpContext的存在。

相關問題