我有一个Singleton类,它使用来自Jon Skeet的线程安全的Singleton模式,如TekPub视频中所示。该类表示MVC 3 UI中下拉列表的参考数据的缓存列表。
为了获取列表数据,该类在我的DAL中的静态类上调用静态方法。
现在我正在进行测试,我想在我的DAL类上实现一个接口,但显然不能,因为它是静态的,并且只有一个静态方法,所以没有可以创建的接口。所以我想删除静态实现,以便我可以进行界面。
通过这样做,我无法从引用类中静态调用该方法,并且因为引用类是具有私有ctor的单例,所以我无法注入该接口。我该如何解决这个问题?如何让我的界面进入引用类,以便我可以使用DI,我可以使用模拟成功测试它?
这是我当前形式的DAL课程
public static class ListItemRepository {
public static List<ReferenceDTO> All() {
List<ReferenceDTO> fullList;
... /// populate list
return fullList;
}
}
这就是我希望它看起来像
public interface IListItemRepository {
List<ReferenceDTO> All();
}
public class ListItemRepository : IListItemRepository {
public List<ReferenceDTO> All() {
List<ReferenceDTO> fullList;
... /// populate list
return fullList;
}
}
这是我的单例引用类,对静态方法的调用是在CheckRefresh
调用
public sealed class ListItemReference {
private static readonly Lazy<ListItemReference> instance =
new Lazy<ListItemReference>(() => new ListItemReference(), true);
private const int RefreshInterval = 60;
private List<ReferenceDTO> cache;
private DateTime nextRefreshDate = DateTime.MinValue;
public static ListItemReference Instance {
get { return instance.Value; }
}
public List<SelectListDTO> SelectList {
get {
var lst = GetSelectList();
lst = ReferenceHelper.AddDefaultItemToList(lst);
return lst;
}
}
private ListItemReference() { }
public ReferenceDTO GetByID(int id) {
CheckRefresh();
return cache.Find(item => item.ID == id);
}
public void InvalidateCache() {
nextRefreshDate = DateTime.MinValue;
}
private List<SelectListDTO> GetSelectList() {
CheckRefresh();
var lst = new List<SelectListDTO>(cache.Count + 1);
cache.ForEach(item => lst.Add(new SelectListDTO { ID = item.ID, Name = item.Name }));
return lst;
}
private void CheckRefresh() {
if (DateTime.Now <= nextRefreshDate) return;
cache = ListItemRepository.All(); // Here is the call to the static class method
nextRefreshDate = DateTime.Now.AddSeconds(RefreshInterval);
}
}
}
答案 0 :(得分:1)
您可以使用基于实例的单例(不基于静态),您可以为此声明接口。
public interface IListItemRepository
{
List<ReferenceDTO> All();
}
public class ListItemRepository : IListItemRepository
{
static IListItemRepository _current = new ListItemRepository();
public static IListItemRepository Current
{
get { return _current; }
}
public static void SetCurrent(IListItemRepository listItemRepository)
{
_current = listItemRepository;
}
public List<ReferenceDTO> All()
{
.....
}
}
现在,您可以模拟IListItemRepository进行测试。
public void Test()
{
//arrange
//If Moq framework is used,
var expected = new List<ReferneceDTO>{new ReferneceDTO()};
var mock = new Mock<IListItemRepository>();
mock.Setup(x=>x.All()).Returns(expected);
ListItemRepository.SetCurrent(mock.Object);
//act
var result = ListItemRepository.Current.All();
//Assert
Assert.IsSame(expected, result);
}
答案 1 :(得分:0)
您使用的是哪种DI框架?根据您的答案,IOC容器应该能够处理单实例,这样您就不必在缓存类中实现自己的单例模式。在您的代码中,您将所有内容视为实例化类,但在DI框架映射中,您将能够指定只应创建一个缓存类实例。
答案 2 :(得分:0)
测试它的一种方法是,如果通过添加额外属性来重构ListItemReference:
public sealed class ListItemReference {
...
public Func<List<ReferenceDTO>> References = () => ListItemRepository.All();
...
private void CheckRefresh() {
if (DateTime.Now <= nextRefreshDate) return;
cache = References();
nextRefreshDate = DateTime.Now.AddSeconds(RefreshInterval);
}
}
然后在你的测试中你可以做到:
ListItemReference listReferences = new ListItemReference();
listReferences.References = () => new List<ReferenceDTO>(); //here you can return any mock data
当然这只是暂时的解决方案,我建议使用IoC / DI摆脱静态。