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

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

我的缓存Class如下:

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

我尝试了如下所示的模拟对象,但无法获得结果。

 [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

    }

有人可以检查一下我向缓存中插入的数据是否正确吗?另外如何在单元测试起订量中验证缓存数据?我是单元测试的新手。

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

更新的场景

考虑使用另一个名为 "Convertor" 的 class,它根据缓存中的成绩 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();
    }
}

对于这种情况,我该如何验证呢?由于此方法是使用缓存来检索值,因此我不确定如何在此处使用模拟。

您似乎误解了模拟的用法。使用模拟的原因是测试依赖 classes 而不必担心依赖性。

假设您有以下 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;
    }
}

为了测试上面的 class 你必须模拟在构造函数中注入到 class 的 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);

我们使用依赖 class 依赖的缓存键 "GradeList" 模拟 ICache 到 return 6 级。我们现在可以测试 MyDependentClass 上的方法是否正确 returns 缓存中的计数。同样,您可以在缓存 return 为空列表或 null 的情况下进行测试,以检查从属 class 的行为在所有情况下是否正确。

在你的例子中你似乎想要测试 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.

Updated scenario

对于更新后的场景,您无法模拟缓存,因为它是通过 CacheFactory 上的静态方法检索的。这可能是对依赖项使用依赖注入的最佳理由之一,而不是在 class 中自己实例化它们或使用静态 classes/methods.

要使 Converter class 可测试,我会做的是将 ICache 注入 class 而不是使用 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 class 的功能。

看来你是概念混淆了

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

Mocking 用于第 3 方 classes 在您要测试的 class 中使用。

事实上你的插入方法也有一个依赖:

private readonly ObjectCache _objCache = MemoryCache.Default;

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

但这次ObjectCache必须继承相关接口才能在Moq lib中使用,即IObjectCache

你应该使用:

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

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

然而 ObjectCache class 继承自 IEnumerable 和 IEnumerable>,因此您不能直接模拟它。如果你真的需要模拟,你应该使用桥接接口(反腐败层)。