所以,我一直在阅读 Working Effectively With Legacy Code。它很棒,涵盖了很多很酷的技术,这些技术也可以“安全”地用于未经测试的代码。
我最喜欢的部分之一是建议在存在麻烦的依赖项时扩展您打算测试的类。例如
class FooBar
{
public List<Foo> ReadFoos()
{
var s=new SqlConnection(Config.SomeConntectionStirng);
s.Read("Foo")....
}
}
被重构为
class FooBar
{
public List<Foo> ReadFoos()
{
var s=MyConnection();
s.Read("Foo")....
}
protected virtual IConnection MyConnection()
{
return new SqlConnection(Config.SomeConnectionString);
}
}
然后为了测试它,您可以像这样从它继承:
class TestableFooBar : FooBar
{
protected override IConnection MyConnection()
{
return new MockConnection();
}
}
并在扩展类上进行测试。
现在,我的问题。可以这样做而不是稍后重构吗?我这样说的主要原因是因为这可能对一些通常难以进行单元测试的代码很有用,同时又不会使调用 API 更难使用。在公共(public) API 上,您肯定希望它尽可能简单。那么,按设计保留是一件好事吗?
最佳答案
从设计的角度来看,“提取和覆盖”可能不是您选择完成的地方,但这取决于您更改现有代码与添加新功能的预算。虽然您可能也不热衷于更改公共(public) API,但有一些技术可以在保留 API 的同时允许更适当的依赖项注入(inject)。我相信这本书讨论了一种这样的技术,它是一个默认构造函数,它提供对重载的依赖(下面的示例),另一种可以简单地使用依赖注入(inject)容器。
public class Foo
{
IDependency dependency;
public Foo() // preserves API
: this(new Dependency())
{
}
internal Foo(IDependency dependency)
{
this.dependency = dependency;
}
注意:在此示例中,我已将第二个构造函数标记为内部构造函数,但您可以将其保留为公共(public)构造函数,特别是如果您希望程序集外部的类可以选择提供该依赖项。为了简单的可测试性,我喜欢 internal 修饰符,因为我会通过“InternalsVisibleTo”程序集属性将内部结构暴露给我的单元测试项目。这使您可以保持 API 的清洁,同时对此类技术以及内部帮助程序类也具有最大的可测试性。
关于c# - 从您打算测试的类继承是否总是次优的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14884921/