Вы закрыли некоторые двери, сделав кеш статичным.
Быстрое и грязное решение:
Поскольку конструктор не может внедрить ваш репозиторий, следующая лучшая вещь - передать его статическому методу.
public static async Task<List<Contact>> GetCodeValuesAsync(IContactRepository repo, int inst, CancellationToken ct)
Если вы сделаете это, возможно, будет лучше переместить управление жизненным циклом хранилища на один уровень вверх.Другими словами, переместите оператор using
в вызывающую сторону:
using(var repo = new ContactRepository())
{
await ContactsCache.GetContactsAsync(repo , It.IsAny<int>(), CancellationToken.None);
}
Тогда в своем тесте вы сможете сделать это:
var mock = new Mock<IContactsRepository>()
.Setup(x => x.LoadAsync(It.IsAny<int>(), CancellationToken.None))
.ReturnsAsync(new List<Contact>(expected));
var actual = await ContactsCache.GetContactsAsync(mock , It.IsAny<int>(), CancellationToken.None);
Предпочтительные решения:
Я предполагаю, что ваш репозиторий отвечает за управление сеансом (отсюда и интерфейс IDisposable).Если у вас есть способ отделить свой интерфейс репозитория от любых ресурсов, которые могут потребоваться для некоторых реализаций, вы можете перейти к подходу с использованием конструктора.
Ваш код будет выглядеть примерно так:
public class ContactsCache : IContactsCache
{
private readonly IContactRepository contactRepo;
public ContactsCache(IContactRepository contactRepo)
{
this.contactRepo = contactRepo;
}
// ...
return await this.contactRepo.LoadAsync(inst, ct).ConfigureAwait(false);
// ...
}
И ваш модульный тест будет выглядеть так:
[TestMethod]
public async void GetContactAsync_WhenCalled_ReturnCodeValuesCache()
{
var expected = new List<Contact>
{
new Contact() {Instance = 1, Name = "Test" }
};
var mock = new Mock<IContactsRepository>()
.Setup(x => x.LoadAsync(It.IsAny<int>(), CancellationToken.None))
.ReturnsAsync(new List<Contact>(expected));
var cache = new ContactsCache(mock);
var actual = await cache .GetContactsAsync(It.IsAny<int>(), CancellationToken.None);
CollectionAssert.AreEqual(actual, expected);
}
Вы также можете рассмотреть вопрос об изменении зависимости между кешем и хранилищем.Другими словами, ваша реализация репозитория может иметь кеш.Это позволяет более динамично выбирать стратегию кэширования.Например, у вас может быть одно из следующих:
var repo = new ContactRepository(new MemoryCache<Contact>())
или
var repo = new ContactsRepository(new NullCache<Contact>())
<- если вам не нужно кэширование в некоторых контекстах. </p>
Этот подход означает, что потребителю вашего хранилища не нужно знать или заботиться о том, откуда поступают данные.Это позволяет вам протестировать ваш механизм кэширования, не требуя в первую очередь хранилища.Конечно, если вы хотите протестировать репозиторий, вам необходимо предоставить ему стратегию кэширования.
Следуя этому подходу, вы также получаете доступ к довольно быстрому решению, поскольку вы можете обернуть свой существующий статический кеш с помощьюКласс, подобный следующему:
public class MemoryCache : ICachingStrategy<Contact>
{
public async Task<List<Contact>> GetCodeValuesAsync(int inst, CancellationToken ct) // This comes from the interface
{
return await ContactsCache.GetContactsAsync(inst, ct); // Just forward the call to the existing static cache
}
}
Вашему хранилищу потребуется некоторая работа, чтобы заставить его учитывать кэш, прежде чем обращаться к базе данных db / file system / remote.
Примечание: если вы new
до «зависимостей» вы больше не выполняете внедрение зависимостей.