使用内部构造函数和私有设置器对域对象进行单元测试
本文关键字:对象 单元测试 设置 内部 构造函数 | 更新日期: 2023-09-27 18:06:27
我在一个名为Tools的项目中定义了一个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的单独项目中有另一个名为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方法编写一个单元测试,但我不确定我想如何解决以下问题:
Person有一个带有XElement参数的内部构造函数方法。我不想在单元测试中手动创建XElement对象。
Person有私人设置(我认为它应该,我不希望工具的用户。客户端修改从API返回的数据)。对Job的额外依赖加剧了这个问题,因为Job具有类似的结构。
我可以想到一些解决方案,但不知道哪一个将是最可维护的随着时间的推移:
- 创建IPerson和IJob接口并使用这些接口的模拟或简单测试实现。
- 暴露公共设置以方便测试(再次,我真的不喜欢这样)。我想我也可以使用内部设置与InternalsVisibleTo属性(不像公共那么糟糕,但仍然不是我想要的)。
- 将XML解析移出构造函数,并让构造函数接受参数firstName、lastName、jobs。构造函数仍然可以是内部的,我只需要在程序集中使用InternalsVisibleTo属性。
我认为你应该把xml解析从Person
移动到一个单独的工厂类,使Person
不可变的值对象类。这样您就不需要模拟它们,您应该能够创建Person
和Job
的真实实例来测试Analyzer
。
我同意你不想让你的setter公开。你现在有了一个不可变的类,你不应该轻易放弃它。
值对象上的接口是可以的,但是它们经常被过度设计。如果有更好的解决方案,请避免。
选择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;}
}