0

我有一个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,但不能以这种方式进行测试。

我怎么能嘲笑它?

4

3 回答 3

2

您需要使用工厂来创建新的WordsRepository. 您可能知道,每当您new在代码中看到这个词时,最好创建一个工厂类来为您提供所请求的实例,因为这样您就可以模拟工厂以在测试时返回您想要的任何实例。

所以你会改变这个:

  _WordsRep = WordsRepositoryFactory.CreateNewRepository(_selectedProject);
于 2012-05-10T14:50:59.567 回答
1

查看托管可扩展性框架。基本上,您有一个接口,该接口上有多个实现。但是,在运行时,您希望能够相互切换这些实现。为此,您不能静态链接到任何一个实现,并且应该允许框架确定为您使用哪一个。

MEF 将允许您 [Import(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

于 2012-05-10T14:52:18.743 回答
0

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

于 2012-05-10T14:55:35.213 回答