我有一个Project
实体,其中包含一组Word
个实体。我的WordsRepository
取决于Project
实体。它应该在项目包含的单词集上运行。
public interface IWordsRepository
{
List<Word> FetchAll();
}
public class WordsRepository : IWordsRepository
{
private readonly Project _project;
public WordsRepository(Project project)
{
if (project == null)
throw new ArgumentNullException("project");
this._project = project;
}
public List<Word> FetchAll()
{
// code for retrieveing words for the project from the database
}
}
现在让我们深入挖掘一下。这是我的ProjectsModel
。
public class ProjectsModel
{
private readonly IProjectsRepository _rep;
private IWordsRepository _wordsRep;
private IProjectsModelObserver _presenter;
// Regular contructor for usual purpose
public ProjectsModel()
{
this._rep = new ProjectsRepository(Program.context);
}
public ProjectsModel(IProjectsRepository repository)
{
this._rep = repository;
}
public virtual void AttachPresenter(IProjectsModelObserver observer)
{
this._presenter = observer;
}
public List<Project> projects
{
get
{
List<Project> tmpList = _rep.FetchAll();
return (tmpList != null) ? tmpList : new List<Project>();
}
}
private Project _selectedProject;
public Project selectedProject
{
get
{
if (_selectedProject == null)
_selectedProject = projects.FirstOrDefault();
return _selectedProject;
}
set
{
if (!projects.Contains(value))
throw new InvalidOperationException("Project not in the Projects list");
_selectedProject = projects[projects.IndexOf(value)];
// Recreating Words repository depending on project
// Here is my issue:
// As I should recreate the Words repository here, passing a new selected project
// how can I mock it and make this class testable?
this._wordsRep = new WordsRepository(this._selectedProject);
if (this._presenter != null)
this._presenter.SelectedProjectChanged(_selectedProject);
}
}
private List<Word> _words;
public List<Word> Words
{
get
{
// Fetching set of words in Project. Just invoking a words repository
}
}
}
当模型的选定项目发生变化时,它应该重新创建WordsRepository
,但这样做是不可测试的。
我该如何嘲笑它?
答案 0 :(得分:2)
您需要使用工厂来创建新的WordsRepository
。正如您可能知道的那样,每当您在代码中看到单词new
时,创建一个可以为您提供该实例的Factory类可能是个好主意,因为这样您就可以模拟工厂返回任何实例想要在测试时间。
所以你会改变这个:
_WordsRep = WordsRepositoryFactory.CreateNewRepository(_selectedProject);
答案 1 :(得分:1)
查看Managed Extensibility Framework。基本上,您在该接口上有一个具有多个实现的单一接口。但是,在运行时,您希望能够相互切换这些实现。为此,您不能静态链接任何一个实现,并应允许框架确定使用哪个实现。
MEF将允许您[导入(typeof(IWordsRepository))]。这将允许您作为部署项放入不同的实现。代码看起来像这样。
[Export(typeof(IWordsRepository))]
public class WordsRepository : IWordsRepository
{
public List<Word> FetchAll()
{
//this class will hit the database
}
}
[Export(typeof(IWordsRepository))]
public class WordsRepositoryTest : IWordsRepository
{
public List<Word> FetchAll()
{
//this class will not go to the database, maybe an xml file or something.
}
}
然后,您的ProjectsModel将使用:
[Import(typeof(IWordsRepository))]
IWordsRepository _repo;
将这两种类型存储在两个不同的dll中,并将测试类型作为单元测试的部署项目放入。它将使用您放入的任何实现。
有关MEF的更多信息: http://msdn.microsoft.com/en-us/library/dd460648.aspx
答案 2 :(得分:0)
我发现你的代码很难理解,但工厂是一种选择,正如Tejs所提到的那样。另一种可能性是没有setter,而是SetSelectedProject(IWordsRepository)方法。在您的特定项目中,这可能是也可能不是。