简体   繁体   English

如何设置Mockobjects并使用Moq单元测试C#进行验证?

[英]How to setup Mockobjects and verify using Moq unit testing C#?

My Cache Class is given below: 我的缓存类如下:

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;
    }
}

And my Interface is 我的界面是

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

I am using CacheFactory to call my InMemoryCache Class: 我正在使用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;
    }
}

I tried Mock Object like below and unable to get the result. 我尝试了如下所示的Mock Object,但无法获得结果。

 [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

    }

Could someone check that my data insertion to cache is correct? 有人可以检查我要缓存的数据是否正确吗? Also how can I verify the cache data in unit test Moq? 另外,如何在单元测试Moq中验证缓存数据? I am new to unit testing. 我是单元测试的新手。

I am not able to verify since I am inserting data to object cache in my implementation class but mocking the interface. 我无法验证,因为我正在将数据插入实现类中的对象缓存中,但模拟了接口。 I suppose if I could relate both, It might be verifying. 我想如果我能将两者联系在一起,那可能是在验证。 Not sure, it's just my guess. 不确定,这只是我的猜测。

Updated Scenario 更新方案

Consider having another class named "Convertor" which retrieves the Grade name based on the Grade ID from cache. 考虑使用另一个名为“ Convertor”的类,该类根据缓存中的成绩ID检索成绩名称。

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();
    }
}

For this case, how can I verify this? 对于这种情况,我该如何验证? Since this method is using cache to retrieve value, I am not sure how to use mock here. 由于此方法使用缓存来检索值,因此我不确定如何在此处使用模拟。

You seem to be misunderstanding the usage of mocks. 您似乎误解了模拟的用法。 The reason for using mocks is to test dependent classes without worrying on the dependencies. 使用模拟的原因是测试依赖类而不必担心依赖关系。

Let's say you have the following class: 假设您有以下课程:

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;
    }
}

In order to test the above class you would have to mock the ICache object that is injected into the class in the constructor. 为了测试上述类,您必须模拟注入到构造函数中的类中的ICache对象。 In this case it would be sensible to use Mock : 在这种情况下,使用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);

We mock the ICache to return 6 grades using the cache key "GradeList", which the dependent class relies on. 我们使用依赖项依赖的缓存键“ GradeList”模拟ICache以返回6个成绩。 We can now test that the method on MyDependentClass correctly returns the count in the cache. 现在,我们可以测试MyDependentClass上的方法正确返回了缓存中的计数。 Likewise, you could make a test where the cache returns an empty list or null to check that the behavior of the dependent class is correct in all cases. 同样,您可以在缓存返回空列表或null地方进行测试,以检查在所有情况下依赖类的行为是否正确。

It seems in your example you want to test the InMemoryCache itself, in which case you would not want to mock anything. 在您的示例中,您似乎想测试InMemoryCache本身,在这种情况下,您不希望模拟任何内容。 I would simply write tests like this: 我只是写这样的测试:

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.

Updated scenario 更新方案

For your updated scenario you cannot mock the cache, as it is retrieved by a static method on the CacheFactory . 对于更新的方案,您无法模拟缓存,因为它是由CacheFactory上的静态方法检索的。 This is probably one of the best reasons to use dependency-injection for your dependencies, instead of instantiating them yourself in the class or instead of using static classes/methods. 这可能是对依赖项使用依赖项注入的最佳原因之一,而不是在类中自己实例化它们或使用静态类/方法。

What I would do to make the Converter class testable, would be to inject ICache into the class instead of using the 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();
    }
}

Now you would be able to mock the ICache and test the functionality of the Convertor class like I described above. 现在,您将能够像上面描述的那样模拟ICache并测试Convertor类的功能。

It seems that you are confused in the conceptions. 您似乎对这些概念感到困惑。

If you want to (unit) test the Insert method of your InMemoryCache class, you definetaly should not mock the InMemoryCache. 如果要(单元)测试InMemoryCache类的Insert方法,则定义为不应模拟InMemoryCache。

Mocking is for the 3rd party classes used in the class you want to test. 模拟适用于您要测试的班级中使用的第三方班级。

In fact you have also a dependency in your insert method: 实际上,您的insert方法也有一个依赖项:

private readonly ObjectCache _objCache = MemoryCache.Default;

So, you should mock the ObjectCache instance somehow. 因此,您应该以某种方式模拟ObjectCache实例。

But this time ObjectCache must inherited from related interface in order to be used in Moq lib, ie IObjectCache 但是这一次ObjectCache必须继承自相关的接口才能在Moq lib中使用,即IObjectCache

And you should use: 您应该使用:

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

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

However ObjectCache class is inherited from IEnumerable and IEnumerable>, so you cannot mock it directly. 但是ObjectCache类是从IEnumerable和IEnumerable>继承的,因此您不能直接对其进行模拟。 You should use a bridge interface (anti corruption layer) if you really need to mock. 如果确实需要模拟,则应使用网桥接口(防破坏层)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM