XUnit如何模拟IMemoryCache ASP.NET Core

时间:2018-11-09 09:46:41

标签: c# unit-testing mocking moq xunit

我了解IMemoryCache.Set是一种扩展方法,因此无法模拟。人们已经针对这种情况提供了解决方法,例如NKosi here提出的解决方法。我想知道如何为我的数据访问层实现这一目标,在这里我的MemoryCache返回一个值,当找不到时,它从db获取数据,将其设置为MemoryCache并返回所需的值。

age

以下是我的单元测试-当然,它不能正常工作,因为我无法模拟IMemoryCache

    public string GetMessage(int code)
    {
        if(myMemoryCache.Get("Key") != null)
        {
            var messages= myMemoryCache.Get<IEnumerable<MyModel>>("Key");
            return messages.Where(x => x.Code == code).FirstOrDefault().Message;
        }

        using (var connection = dbFactory.CreateConnection())
        {
            var cacheOptions = new MemoryCacheEntryOptions { SlidingExpiration = TimeSpan.FromHours(1) };
            const string sql = @"SELECT Code, Message FROM MyTable";

            var keyPairValueData = connection.Query<KeyPairValueData>(sql);

            myMemoryCache.Set("Key", keyPairValueData, cacheOptions );

            return keyPairValueData.Where(x => x.Code == code).FirstOrDefault().Message;
        }
    }

1 个答案:

答案 0 :(得分:1)

我要说的第一件事是为什么不使用真实内存缓存?这样可以更好地验证该行为,而无需模拟它:

snippets

如果您真的想模拟它,请参阅以下指南:

由于它是扩展方法,因此需要确保可以按原样调用它。您的情况是扩展方法将调用该模拟。由于您没有提供预期的行为,因此可能会失败。

您需要查看扩展方法的代码,检查其访问范围,然后确保您的模拟符合预期的行为。该代码在此处可用: https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L77

这是代码:

// Arrange
var memCache = new MemoryCache("name", new NameValueCollection());

//Act
var result = new DataService(dbConnectionFactoryMock.Object, memCache).GetMessage(1000);

// Assert: has been added to cache
memCache.TryGetValue("Key", out var result2);
Assert.Equal("Some message", result2);

// Assert: value is returned
Assert.Equal("Some message", result);

因此,您可以从中看到它访问public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options) { using (var entry = cache.CreateEntry(key)) { if (options != null) { entry.SetOptions(options); } entry.Value = value; } return value; } 并期望有一个对象。然后,它调用CreateEnty并在条目上分配SetOptions

您可以这样模拟:

Value

执行此操作时,扩展方法将在模拟对象上调用,并将返回模拟的条目。您可以修改实现并使其做任何您想做的事情。