模型依赖于两个存储库。如何模仿其中一个?

时间:2012-05-10 14:36:43

标签: c# .net repository-pattern mvp passive-view

我有一个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,但这样做是不可测试的。

我该如何嘲笑它?

3 个答案:

答案 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

有关部署项目的更多信息: http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.deploymentitemattribute(v=vs.80).aspx

答案 2 :(得分:0)

我发现你的代码很难理解,但工厂是一种选择,正如Tejs所提到的那样。另一种可能性是没有setter,而是SetSelectedProject(IWordsRepository)方法。在您的特定项目中,这可能是也可能不是。