0

我在一个名为 Tools.Client 的项目中定义了一个 Person 类,它是一个 Web 服务 API 的包装器。Person 是使用服务返回的 XML 构造的。

public class Person
{
  internal Person(XElement personElement)
  {
    this.FirstName = personElement.Element("first_name").Value;
    this.LastName = personElement.Element("last_name").Value;

    this.Jobs = new List<Job>();
    foreach (var jobElement in personElement.Elements("jobs"))
    {
      this.Jobs.Add(new Job(jobElement));
    }
  }

  public string FirstName {get; private set;}
  public string LastName {get; private set;}
  public ICollection<Job> {get; private set;}
}

我在一个名为 Tools.Analysis 的单独项目中有另一个名为 Analyzer 的类,它包含针对从 API 客户端检索到的数据运行的分析逻辑。

private readonly ICollection<Person> _people;
public Analyzer(ICollection<Person> people)
{
  _people = people;
}

public AnalysisResult Analyze()
{
  var result = new AnalysisResult();

  foreach (var person in _people)
  {
    // do some analysis, store data in the result
  }

  return result;
}

我想为 Analyzer 类的 Analyze 方法编写单元测试,但我不确定如何解决以下问题:

  1. Person 有一个带有 XElement 参数的内部构造方法。我不想在我的单元测试中创建手动 XElement 对象。

  2. Person 有私人设置器(我认为应该如此,我不希望 Tools.Client 的用户更改从 API 返回的数据)。对具有类似结构的 Job 的额外依赖加剧了这个问题。

我可以想到一些解决方案,但不知道随着时间的推移哪个是最可维护的:

  1. 创建 IPerson 和 IJob 接口并使用这些接口的模拟或简单测试实现。
  2. 公开公共设置器以便于测试(同样,我不太喜欢这样)。我想我也可以使用带有 InternalsVisibleTo 属性的内部设置器(不像公共那么糟糕,但仍然不是我想要的)。
  3. 将 XML 解析移到构造函数之外,并让构造函数获取参数 firstName、lastName、jobs。构造函数仍然可以是内部的,我只需要在我的程序集中使用 InternalsVisibleTo 属性。
4

2 回答 2

4

我认为您应该将 xml 解析从Person一个单独的类似工厂的类中移出,制作Person不可变的类似值对象的类。这样您就不需要模拟它们,您应该能够创建 和 的真实实例以Person进行Job测试Analyzer

于 2013-03-28T03:46:44.970 回答
0

我同意你不想让你的二传手公开。你现在有一个不可变的类,你不应该轻易放弃它。

值对象上的接口是可以的,但它们通常是过度设计的。如果有更好的解决方案,请避免这种情况。

选择选项 3。 XML 解析实际上是另一个类的责任;Person应该只是一个数据对象。

如果你不能这样做,至少创建一个没有它的构造函数。这不是很好,因为您的构造函数可能会出现分歧(它们不能轻易地相互调用),但它比您所在的位置要好得多。

public class Person
{
  internal Person(XElement personElement)
  {
    this.FirstName = personElement.Element("first_name").Value;
    this.LastName = personElement.Element("last_name").Value;

    this.Jobs = new List<Job>();
    foreach (var jobElement in personElement.Elements("jobs"))
    {
      this.Jobs.Add(new Job(jobElement));
    }
  }

  internal Person(string firstName, string lastName, ICollection<Job> jobs)
  {
     //set properties
  }

  public string FirstName {get; private set;}
  public string LastName {get; private set;}
  public ICollection<Job> {get; private set;}
}
于 2013-03-28T03:55:44.820 回答