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

本文关键字:Moq 单元测试 验证 何设置 设置 Mockobjects | 更新日期: 2023-09-27 18:08:29

我的缓存类如下:

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中的缓存数据?我是单元测试新手。

我无法验证,因为我在我的实现类中插入数据到对象缓存,但模拟接口。我想如果我能把两者联系起来,就能证实了。不确定,这只是我的猜测。

更新场景

考虑另一个名为"converter"的类,它根据缓存中的Grade ID检索Grade名称。

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

对于这种情况,我如何验证它?由于此方法使用缓存来检索值,因此我不确定如何在这里使用mock。

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

您似乎误解了mock的用法。使用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类的功能,就像我上面描述的那样。

你似乎混淆了概念。

如果你想(单元)测试InMemoryCache类的Insert方法,你绝对不应该模拟InMemoryCache。

mock是用于你想测试的类中使用的第三方类。

实际上你在insert方法中也有一个依赖项:

private readonly ObjectCache _objCache = MemoryCache.Default;

因此,应该以某种方式模拟ObjectCache实例。

但是这次ObjectCache必须从相关接口继承,以便在Moq库中使用,即IObjectCache

你应该使用:

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

但是ObjectCache类继承自IEnumerable和IEnumerable>,所以你不能直接模拟它。如果您确实需要模拟,则应该使用桥接接口(防损坏层)。