C# 模拟静态异步类的单元测试和依赖注入
我有一个联系人列表的静态异步缓存。在缓存内部,我调用我的存储库从后端获取数据。我想模拟ContactsRepository,但我需要将存储库作为参数传递,并使用依赖项注入 根据文档,它不会工作,因为我需要一个类的实例来使用依赖项注入C# 模拟静态异步类的单元测试和依赖注入,c#,.net,unit-testing,testing,C#,.net,Unit Testing,Testing,我有一个联系人列表的静态异步缓存。在缓存内部,我调用我的存储库从后端获取数据。我想模拟ContactsRepository,但我需要将存储库作为参数传递,并使用依赖项注入 根据文档,它不会工作,因为我需要一个类的实例来使用依赖项注入 public interface IContactsCache { Task<List<Contact>> GetContactsAsync(int inst, CancellationToken ct); } public cla
public interface IContactsCache
{
Task<List<Contact>> GetContactsAsync(int inst, CancellationToken ct);
}
public class ContactsCache : IContactsCache
{
private static readonly object _syncRoot = new object();
private static readonly Dictionary<int, Task<List<Contact>>> _contactsTasks = new Dictionary<int, Task<List<Contact>>>();
public static Task<List<Contact>> GetContactsAsync(int inst)
{
return GetContactsAsync(inst, CancellationToken.None);
}
public static async Task<List<Contact>> GetCodeValuesAsync(int inst, CancellationToken ct)
{
Task<List<Contact>> task;
lock (_syncRoot)
{
if (_contactsTasks.ContainsKey(inst) && (_contactsTasks[inst].IsCanceled || _contactsTasks[inst].IsFaulted))
{
_contactsTasks.Remove(inst);
}
if (!_contactsTasks.ContainsKey(inst))
{
_contactsTasks[inst] = Task.Run(async () =>
{
using (var rep = new ContactsRepository())
{
return await rep.LoadAsync(inst, ct).ConfigureAwait(false);
}
});
}
task = _contactsTasks[inst];
}
var res = await task.ConfigureAwait(false);
lock (_syncRoot)
{
return res != null ? res.ToList() : null;
}
}
Task<List<CodeValue>> IContactsCache.GetContactsAsync(int inst, CancellationToken ct)
{
return GetContactsAsync(inst, ct);
}
}
公共接口IContactsCache
{
任务GetContactsAsync(int inst,CancellationToken ct);
}
公共类ContactsCache:IContactsCache
{
私有静态只读对象_syncRoot=新对象();
专用静态只读词典_contactsTasks=new Dictionary();
公共静态任务GetContactsAsync(int inst)
{
返回GetContactsAsync(inst,CancellationToken.None);
}
公共静态异步任务GetCodeValuesAsync(int inst,CancellationToken ct)
{
任务;
锁定(\u syncRoot)
{
如果(_contactsTasks.ContainsKey(inst)&(_contactsTasks[inst].IsCanceled | | | u contactsTasks[inst].IsFaulted))
{
_拆下接触器(安装);
}
如果(!_contactsTasks.ContainsKey(inst))
{
_contactsTasks[inst]=任务。运行(异步()=>
{
使用(var rep=new contacts repository())
{
返回wait rep.LoadAsync(inst,ct).configurewait(false);
}
});
}
任务=_contactsTasks[inst];
}
var res=等待任务。配置等待(false);
锁定(\u syncRoot)
{
return res!=null?res.ToList():null;
}
}
任务IContactsCache.GetContactsAsync(int inst,CancellationToken ct)
{
返回GetContactsAsync(仪器、ct);
}
}
最后,我希望有这种用法,但我不知道如何更改cache类,否则任何其他帮助都会非常有用
[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 actual = await ContactsCache.GetContactsAsync(It.IsAny<int>(), CancellationToken.None);
CollectionAssert.AreEqual(actual, expected);
}
[TestMethod]
public async void GetContactAsync\u调用时\u ReturnCodeValuesCache()
{
var预期值=新列表
{
新联系人(){Instance=1,Name=“Test”}
};
var mock=new mock()
.Setup(x=>x.LoadAsync(It.IsAny(),CancellationToken.None))
.ReturnsAsync(新列表(预期));
var actual=await ContactsCache.GetContactsAsync(It.IsAny(),CancellationToken.None);
收款资产等于(实际、预期);
}
但它不能工作,我不知道如何正确地编写单元测试
我有很多这样的缓存,我正在使用这样的存储库。在这种情况下,有没有标准或最佳实践来测试静态异步缓存以及如何模拟存储库?通过使缓存为静态,您已经关闭了一些大门 快速而肮脏的解决方案: 因为您不能将构造函数注入存储库,所以下一个最好的方法是将其传递给静态方法
public static async Task<List<Contact>> GetCodeValuesAsync(IContactRepository repo, int inst, CancellationToken ct)
然后在测试中,您将能够执行以下操作:
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);
您的单元测试将如下所示:
[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);
}
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
}
}
[TestMethod]
public async void GetContactAsync\u调用时\u ReturnCodeValuesCache()
{
var预期值=新列表
{
新联系人(){Instance=1,Name=“Test”}
};
var mock=new mock()
.Setup(x=>x.LoadAsync(It.IsAny(),CancellationToken.None))
.ReturnsAsync(新列表(预期));
var缓存=新的ContactsCache(模拟);
var actual=wait cache.GetContactsAsync(It.IsAny(),CancellationToken.None);
收款资产等于(实际、预期);
}
您也可以考虑反转缓存和存储库之间的依赖关系。换句话说,您的存储库实现可以有一个缓存。这允许您更动态地选择缓存策略。例如,您可以具有以下任一项:
public class ContactsCache : IContactsCache
{
private readonly IContactRepository contactRepo;
public ContactsCache(IContactRepository contactRepo)
{
this.contactRepo = contactRepo;
}
// ...
return await this.contactRepo.LoadAsync(inst, ct).ConfigureAwait(false);
// ...
}
var repo=new ContactRepository(new MemoryCache())
或
var repo=new contacts repository(new NullCache())
ContactsCache是否需要是静态的?您使用的模拟框架是什么?看起来像是Moq,但我不想在这个问题上加上错误的标签……为什么要让这个类是静态的?让它成为一门普通的课。DependencyInjection的整个工作就是为任何需要它的代码提供缓存类实例。是的,我使用的是MoqI,它需要静态才能在许多地方使用。它是用于缓存目的的,让它保持静态非常有用。我可以成为辛格尔顿,但不确定这是否有帮助。
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
}
}