2016-09-23 130 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