Search code examples
c#asp.net-mvcdependency-injectionmoqmemorycache

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:

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.

 [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? 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.

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.


Solution

  • 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. In this case it would be sensible to use 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. We can now test that the method on MyDependentClass correctly returns the count in the cache. 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.

    It seems in your example you want to test the InMemoryCache itself, in which case you would not want to mock anything. 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. 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:

    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.