XUnit, как издеваться над IMemoryCache ASP.NET Core - PullRequest
0 голосов
/ 09 ноября 2018

Я понимаю, что IMemoryCache.Set - это метод расширения, поэтому его нельзя сменить. Люди предоставили обходные пути для такой ситуации, например, как у НКоси здесь . Мне интересно, как я могу добиться этого для моего уровня доступа к данным, где мой MemoryCache возвращает значение, а когда он не найден, он получает данные из БД, задает для него значение MemoryCache и возвращает требуемое значение.

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

Ниже приведен мой модульный тест - и, конечно, он не работает, так как я не могу издеваться над IMemoryCache

    [Fact]
    public void GetMessage_ReturnsString()
    {
        //Arrange
        // Inserting some data here to the InMemoryDB

        var memoryCacheMock = new Mock<IMemoryCache>();

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

        //assert xunit
        Assert.Equal("Some message", result);
    }

1 Ответ

0 голосов
/ 09 ноября 2018

Первое, что я бы сказал, почему бы не использовать реальный кэш памяти? Было бы намного лучше проверить поведение, и нет нужды издеваться над ним:

// 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);

Если вы действительно хотите сделать это, вот руководство, как это сделать:

Поскольку это метод расширения, вам нужно убедиться, что он может быть вызван как есть. Что происходит в вашем случае, так это то, что метод расширения вызовет макет. Поскольку вы не предоставляете ожидаемого поведения, оно, вероятно, завершится ошибкой.

Вам нужно посмотреть на код метода расширения, проверить, к чему он обращается, а затем убедиться, что ваш макет соответствует ожидаемому поведению. Код доступен здесь: https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L77

Это код:

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.

Вы можете издеваться над этим так:

var entryMock = new Mock<ICacheEntry>();
memoryCacheMock.Setup(m => m.CreateEntry(It.IsAny<object>())
               .Returns(entryMock.Object);

// maybe not needed
entryMock.Setup(e => e.SetOptions(It.IsAny<MemoryCacheEntryOptions>())
         ...

Когда вы сделаете это, для макета будет вызван метод расширения, который вернет имитируемую запись. Вы можете изменить реализацию и заставить ее делать что угодно.

...