4

我正在使用 TDD 进行我的第一个项目,并且在继承方面遇到了一些障碍。

例如,如果我有这样的东西

public interface IComponent
{
    void MethodA();

    void MethodB();
}

public class Component : IComponent
{
    public virtual void MethodA()
    {
        // Do something
    }

    public virtual void MethodB()
    {
        // Do something
    }
}

public class ExtendedComponent : Component
{
    public override void MethodA()
    {
        base.MethodA();

        // Do something else
    }
}

那么我不能单独测试 ExtendedComponent,因为它依赖于 Component。

但是,如果我像这样使用合成来创建 ExtendedComponent

public class ExtendedComponent : IComponent
{
    private readonly IComponent _component;

    public ComponentB(IComponent component)
    {
        _component = component;
    }

    public virtual void MethodA()
    {
        _component.MethodA();

        // Do something else
    }

    public virtual void MethodB()
    {
        _component.MethodB();
    }
}

我现在可以通过模拟包装的 IComponent 来单独测试 ExtendedComponent。

这种方法的缺点是,如果我想向 IComponent 添加新方法,那么我必须将新方法添加到 Component 和 ExtendedComponent 以及可能有很多的任何其他实现。使用继承我可以将新方法添加到基本组件中,它不会破坏其他任何东西。

我真的希望能够干净地进行测试,所以我更喜欢组合路线,但我担心能够进行单元测试并不是总是使用组合而不是继承的正当理由。此外,在基础级别添加功能将需要创建许多繁琐的委托方法。

我真的很感谢其他人如何解决这类问题的一些建议

4

4 回答 4

2

您无需担心“孤立地”测试扩展组件,因为它不“依赖”组件,它是一个组件(至少它是您编写它的方式)。

您最初为组件类编写的所有测试仍然很好,并且还测试了扩展类中所有未更改的行为。您需要做的就是编写新的测试来测试扩展组件中添加的功能。

public class ComponentTests{
  [Fact]
  public void MethodADoesSomething(){
    Assert.xxx(new Component().MethodA());//Tests the component class
  }

  [Fact]
  public void MethodBDoesSomething(){
    Assert.xxx(new Component().MethodB());//Tests the component class
  }
}

public class ExtendedComponentTests{
  [Fact]
  public void MethodADoesSomething(){
    Assert.xxx(new ExtendedComponent().MethodA());//Tests the extended component class
  }
}

您可以从上面看到,MethodA 功能已针对组件和扩展组件进行了测试。而新功能仅针对 ExtendedComponent 进行了测试。

于 2012-10-17T10:50:40.033 回答
2

您使用组合的方法实际上是大多数编译器实现继承的方式,因此您除了付出沉重的成本(大量的样板代码)之外什么也得不到。因此,当存在 is-a 关系时坚持继承,当存在 has-a 关系时坚持组合(这些当然既不是黄金也不是唯一的规则)

于 2012-10-17T10:50:01.560 回答
2

这里的关键思想是在单元测试方面也可以有继承。我在这种情况下使用以下方法。我将有一个单元测试用例的并行继承层次结构。例如

[TestClass]
public abstract class IComponentTest
{
    [TestMethod]
    public void TestMethodA()
    {
         // Interface level expectations.
    }
    [TestMethod]
    public void TestMethodB()
    {
         // Interface level expectations.
    }
}

[TestClass]
public class ComponentTest : IComponentTest
{
    [TestMethod]
    public void TestMethodACustom()
    {
        // Test Component specific test, not general test
    }
    [TestMethod]    
    public void TestMethodBCustom()
    {
        // Test Component specific test, not general test
    }
}
[TestClass]
public class ExtendedComponent : ComponentTest 
{
    public void TestMethodACustom2()
    {
        // Test Component specific test, not general test
    }
}

每个抽象测试类或具体类都在其自己的级别上处理期望。因此可扩展和可维护。

于 2012-10-17T11:01:00.140 回答
0

你是对的 - 在不合适的地方使用组合而不是继承不是要走的路。根据您在此处提供的信息,尚不清楚哪个更好。问问自己在这种情况下哪一个更合适。通过使用继承,您可以获得方法的多态性虚拟化。如果你使用组合,你就可以有效地将你的“前端”逻辑与孤立的“后端”逻辑分开——这种方法更容易,因为更改底层组件不会对其余代码产生连锁反应,因为继承经常发生。

总而言之,这不应该影响您测试代码的方式。有许多可用的测试框架,但这不应该影响您选择的设计模式。

于 2012-10-17T10:52:42.697 回答