仅出于编写更好的单元测试的目的而将具体的类数据公开为仅获取公共属性是否有害?
通常,我宁愿保留与private readonly
一样多的数据。这确保使用者不绑定到所保存的数据,而绑定到类型提供的行为。我最近注意到的一个(潜在)问题是为实现接口的工厂类型编写好的单元测试。请考虑以下说明性示例:
public interface IComponent
{
void DoStuff();
}
public interface IComponentDependency
{
void HelpInDoingStuff(int data);
}
public interface IFactory
{
IComponent CreateComponent(IComponentDependency dependency)
}
public class SpecialComponent : IComponent
{
private readonly int someOtherDependency;
private readonly IComponentDependency dependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
public void DoStuff()
{
dependency.HelpInDoingStuff(someOtherDependency);
}
}
public class SpecialComponentFactory : IFactory
{
private readonly int someOtherDependency;
public SpecialComponentFactory(int someOtherDependency)
{
this.someOtherDependency = someOtherDependency;
}
public IComponent CreateComponent(IComponentDependency dependency)
{
return new SpecialComponent(dependency, this.someOtherDependency);
}
}
为SpecialComponentFactory
编写测试时,唯一可以公开测试的是SpecialComponentFactory
返回SpecialComponent
的实例。如果我还想确认SpecialComponent
确实是由someOtherDependency
构造的,或者实际上,他是由指定的dependency
构造的,该怎么办。为此,我们可以使它们成为protected
和子类SpecialComponent
的子类,然后通过测试伪造将其公开公开:
public class SpecialComponent
{
protected readonly int someOtherDependency;
protected readonly IComponentDependency dependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
public void DoStuff()
{
dependency.HelpInDoingStuff(someOtherDependency);
}
}
public class FakeSpecialComponent : SpecialComponent
{
public FakeSpecialComponent(IComponentDependency dependency, int someOtherDependency)
: base(dependency, someOtherDependency)
{
this.dependency = dependency;
this.someOtherDependency = someOtherDependency;
}
}
但这对我来说似乎并不理想。另一种选择是将依赖项公开为public
。尽管这可以使测试断言所使用的依赖项是正确的,但是这也与我长期以来一直认为将数据保持为私有并仅公开行为的信念背道而驰。
public class SpecialComponent
{
public int SomeOtherDependency;
public IComponentDependency Sependency;
public SpecialComponent(IComponentDependency dependency, int someOtherDependency)
{
Sependency = dependency;
SomeOtherDependency = someOtherDependency;
}
public void DoStuff()
{
Dependency.HelpInDoingStuff(SomeOtherDependency);
}
}
如果整个解决方案实际上从未直接引用SpecialComponent
,而是始终引用IComponent
,则在将依赖项公开为public
的第二种选择中是否有任何危害(或气味)仅获取属性。但是现在,我与另一个长期存在的信念背道而驰,那就是仅出于单元测试的目的而修改成员的可访问性。
将private readonly
字段实际变为我的具体类型的public
只读属性是否有任何危害?我正在尝试将其合理化,因为SpecialComponent
的使用者仅引用IComponent
,因此他们将无法直接访问数据,但是测试可以简单地转换为SpecialComponent
并执行所需的断言语句。
我是否想过所有这些? :)
编辑:我已经更新了上面的代码示例,其中包括IComponent
的方法声明和实现以“填写”示例。如下面的注释中所述,我可以对从IComponent
返回的SpecialComponentFactory.CreateComponent
实例进行某种方式的单元测试,这将证明使用了必需的依赖项,从而提供了覆盖范围,但是对我来说听起来像最不可接受的选择,因为对SpecialComponentFactory
的单元测试将不必要地测试SpecialComponent
的实现,从而为它提供了在其自身实现之外失败的原因,并将SpecialComponentFactory
测试耦合到SpecialComponent
答案 0 :(得分:1)
这实际上是进行单元测试的症状之一。
我会说工厂是使用IComponent
的类的实现细节。
工厂将通过针对该类的测试进行测试。
我不在乎这些测试的分类方式(单元,集成等)-我只在乎我的测试运行缓慢或设置复杂。
因此,我将仅嘲笑会使我的测试变慢或变得复杂的事情。逻辑将在应用程序边界内进行测试,从而使测试保持快速。
例如,在Web应用程序中,它可以是Controller-> Database(内存中或模拟的)。
将单位视为“行为单位”。
写作单元测试仅是因为应该对每个课程进行测试-可以认为这是浪费时间。因为内部设计的任何变化都将迫使我重写单元测试或删除过时的或编写新的单元测试,所以仅适用于课堂,我认为这对他而言是可用的。