2016-09-23 98 views
0

有人可以幫助我如何設置Mockobjects並驗證使用Moq單元測試c#嗎?單元測試問題Mocking Moq

我的Cache類是如下:

public class InMemoryCache : ICache 
{ 
    private readonly ObjectCache _objCache = MemoryCache.Default;  

    public void Insert<T>(string cacheKey, T value) 
    { 
     _objCache.Add(cacheKey, value, DateTimeOffset.MaxValue); 
    } 
    public T Get<T>(string cacheKey) 
    { 
     if (cacheKey != null) 
      return (T)Convert.ChangeType(_objCache.Get(cacheKey), typeof(T)); 
     else 
      return default(T); 
    } 
    public bool Exists<T>(string cacheKey) 
    { 
     return _objCache.Get(cacheKey) != null; 
    } 
} 

我的界面是

public interface ICache 
{ 
    void Insert<T>(string cacheKey, T value); 
    T Get<T>(string cacheKey); 
    bool Exists<T>(string cacheKey);   
} 

我使用CacheFactory打電話給我InMemoryCache類:

public class CacheFactory 
{ 
    public static ICache Cache { get; set; } 

    public static void Insert<T>(string cacheKey, T value) 
    { 
     Cache.Insert<T>(cacheKey, value); 
    } 

    public static T Get<T>(string cacheKey) 
    { 
     return Cache.Get<T>(cacheKey); 
    } 

    public static bool Exists<T>(string cacheKey) 
    { 
     return Cache.Get<T>(cacheKey) != null; 
    } 
} 

我試圖仿製對象像下面一樣,無法得到結果。

[Fact()] 
    public void CacheManager_Insert_Test() 
    { 
     string cacheKey = "GradeList"; 

     Mock<ICache> mockObject = new Mock<ICache>(); 
     List<Grade> grades = new List<Grade> 
     { 
      new Grade() {Id = 1, Name = "A*"}, 
      new Grade() {Id = 2, Name = "A"}, 
      new Grade() {Id = 3, Name = "B"}, 
      new Grade() {Id = 4, Name = "C"}, 
      new Grade() {Id = 5, Name = "D"}, 
      new Grade() {Id = 6, Name = "E"} 
     }; 

     var mockedObjectCache = new Mock<ICache>(); 
     // Setup mock's Set method 

     mockedObjectCache.Setup(m => m.Insert(cacheKey, grades)); 

     // How to get the Mocked data and verify it here 

    } 

有人能幫我,我的數據插入緩存是正確的嗎?還幫助我如何驗證單元測試Moq中的緩存數據。我是單元測試新手,如果需要的話更正我的代碼。我無法驗證,因爲我正在將數據插入到實現類中的對象緩存中,但是卻嘲弄了接口。我想,如果我可以將兩者聯繫起來,那可能是驗證。不知道,它只是我的猜測。我很抱歉,如果這是一個愚蠢的問題,但你的幫助真的很感激。

請讓我知道你是否需要進一步的細節。

問候, Viswa五

更新方案:

考慮其命名爲 「轉換器」,它基於檢索從緩存中的等級標識的等級名稱的另一個類。

public class Convertor 
{ 
    // Gets Grade ID and Displays Grade Name 
    public string GetGradeName(int gradeId) 
    { 
     var gradeList = CacheFactory.Cache.Get<List<Grade>>(CacheKeys.Grades); 
     return gradeList.Where(x => x.Id == gradeId).Select(x => x.Name).FirstOrDefault(); 
    } 
} 

對於這種情況,你能告訴我如何驗證這個?由於此方法使用緩存來檢索值,因此我不確定如何在此處使用模擬。非常感謝您的幫助。謝謝。

+0

看到標記的答案http://stackoverflow.com/questions/5864076/mocking-static-methods –

+0

嗨Ioana,感謝您的更新。我檢查了鏈接。它告訴我們嘲笑靜態方法是不可能的。但是,在我的實現類InMemoryCache中,它只是非靜態的。所以,我不覺得有什麼麻煩來模擬。 – Viswa

回答

2

你似乎誤解了嘲笑的用法。使用mock的原因是測試依賴類而不必擔心依賴關係。

比方說,你有下面的類:

public class MyDependentClass { 
    private readonly ICache _cache; 
    public MyDependentClass(ICache cache) 
    { 
     _cache = cache; 
    } 

    public int CountGradesInCache() 
    { 
     // Behavior depends on what's in the _cache object 
     return _cache.Get<List<Grade>>("GradeList").Count; 
    } 
} 

爲了測試上面的類,你將不得不嘲笑注入到在構造函數的類ICache對象。在這種情況下,這將是明智的使用Mock

string cacheKey = "GradeList"; 

    Mock<ICache> mockObject = new Mock<ICache>(); 
    List<Grade> grades = new List<Grade> 
    { 
     new Grade() {Id = 1, Name = "A*"}, 
     new Grade() {Id = 2, Name = "A"}, 
     new Grade() {Id = 3, Name = "B"}, 
     new Grade() {Id = 4, Name = "C"}, 
     new Grade() {Id = 5, Name = "D"}, 
     new Grade() {Id = 6, Name = "E"} 
    }; 

    var mockedObjectCache = new Mock<ICache>(); 
    // Setup mock's Set method 

    mockedObjectCache.Setup(m => m.Get(It.Is<string>(cacheKey))).Return(grades); 

    // Test that the dependent class acts correctly according to the grades that exist in the cache 
    var myDependentClass = new myDependentClass(mockedObjectCache.Object); 
    var gradesCount = myDependentClass.CountGradesInCache(); 

    Assert.AreEqual(6, gradesCount); 

我們嘲笑ICache返回使用緩存鍵「GradeList」,這依賴類依靠6級。我們現在可以測試MyDependentClass上的方法正確返回緩存中的計數。同樣,您可以在緩存返回一個空列表或null以檢查依賴類的行爲在所有情況下都是正確的情況下進行測試。

在你的例子中你似乎想測試InMemoryCache本身,在這種情況下你不想嘲笑任何東西。我只想寫測試是這樣的:

var cache = new InMemoryCache(); 
var list = new List<Grade> { ... }; 
var cache.Insert("GradeList", list); 

var cachedList = cache.Get<List<Grade>>("GradeList"); 

// Assert that the list retrieved from cache has the expected values. 

更新方案

爲您更新的情況下,你不能嘲笑緩存,因爲它是由在CacheFactory一個靜態方法檢索。這可能是爲依賴項使用依賴注入的最佳理由之一,而不是在類中自己實例化它們,而不是使用靜態類/方法。

我會怎麼做,使Converter類可測試,可將注入ICache到類而不是使用CacheFactory

public class Convertor 
{ 
    private readonly ICache _cache; 

    public Convertor(ICache cache) 
    { 
     _cache = cache; 
    } 

    public string GetGradeName(int gradeId) 
    { 
     var gradeList = _cache.Get<List<Grade>>(CacheKeys.Grades); 
     return gradeList.Where(x => x.Id == gradeId).Select(x => x.Name).FirstOrDefault(); 
    } 
} 

現在你就可以嘲笑ICache和測試的功能像我上面描述的那樣的Convertor類。

+0

嗨,感謝您的更新。還有一個疑問。 – Viswa

+0

您能否檢查「更新後的情景」並讓我知道如何模擬數據?我非常感謝你的迴應。提前致謝。 – Viswa

+0

謝謝tahatmat。早些時候,我沒有創建任何依賴類來模擬它,而是嘗試模擬同一個類。現在我瞭解了嘲笑概念。謝謝你的解釋。 – Viswa

0

看來你對這些概念感到困惑。

如果您想要(單元)測試InMemoryCache類的Insert方法,那麼definetaly不應該模擬InMemoryCache。

模擬適用於您想要測試的課程中使用的第三方課程。

事實上,你也有你的插入方法的依賴:

private readonly ObjectCache _objCache = MemoryCache.Default; 

所以,你應該以某種方式嘲弄ObjectCache實例。

但起訂量LIB要使用這個時候ObjectCache必須從相關的界面繼承了順序,即IObjectCache

而且你應該使用:

var mockObjectCache = new Mock<IObjectCache>(); 
... 

//Verifying: 
mockObjectCache.Verify(cache => cache.Add(It.IsAny<string>())).Should().Be(true); 

但是ObjectCache類是自IEnumerable和IEnumerable繼承> ,所以你不能直接嘲笑它。如果你真的需要模擬,你應該使用橋接口(反腐敗層)。

+1

好的解釋!感謝更新! – Viswa

+1

謝謝Skynyrd!我也接受這個解決方案,因爲這有助於我理解我所犯的錯誤。我試圖嘲笑InMemoryCache對象,而不檢查我使用的ObjectCache的依賴性。我也明白它不是嘲笑InMemoryCache類的正確方法。 – Viswa

+0

我很高興你能更好地理解維斯瓦,祝你好運! – skynyrd