从要测试的类继承总是次优的吗

本文关键字:继承 测试 | 更新日期: 2023-09-27 17:59:10

所以,我一直在阅读《有效使用遗留代码》。它非常棒,涵盖了许多很酷的技术,在未经测试的代码上使用这些技术也是"安全的"。

关于它,我最喜欢的部分之一是建议在存在麻烦的依赖关系时扩展要测试的类。例如

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更难使用。在公共API上,您肯定希望它尽可能简单。那么,这是一个可以保留的设计吗?

从要测试的类继承总是次优的吗

从设计的角度来看,"提取和覆盖"可能不是选择完成的地方,但它取决于您更改现有代码的预算,而不是添加新功能。虽然您可能也不热衷于更改公共API,但有一些技术可以保留API,同时允许更正确的依赖项注入。我相信这本书讨论了一种这样的技术,它是一个默认的构造函数,提供对重载的依赖(下面的示例),另一种可以简单地使用依赖注入容器。

public class Foo
{
     IDependency dependency;
     public Foo() // preserves API
          : this(new Dependency())
     {
     }
     internal Foo(IDependency dependency) 
     {
         this.dependency = dependency;
     }

注意:在这个示例中,我已经将第二个构造函数标记为内部构造函数,但您可以将其保留为公共构造函数,特别是如果您希望程序集之外的类可以选择提供该依赖项的话。对于简单的可测试性,我喜欢内部修饰符,因为我会通过"InternalsVisibleTo"程序集属性向单元测试项目公开内部。这允许您保持API的干净,但也可以最大限度地测试此类技术,以及内部助手类。