单元测试模板方法设计模式

J. *_* Ed 7 language-agnostic tdd unit-testing

假设以下模板方法实现:

public abstract class Abs
  {
    void DoYourThing()
    {
      log.Info("Starting");
      try
      {
        DoYourThingInternal();
        log.Info("All done");
      }
      catch (MyException ex)
      {
        log.Error("Something went wrong here!");
        throw;
      }
    }

    protected abstract void DoYourThingInternal();

  }
Run Code Online (Sandbox Code Playgroud)

现在,有很多关于如何测试Abs类的信息,并确保DoYourThingInternal调用它.
但是,假设我想测试我的Conc课程:

 public class Conc : Abs
  {
    protected override void DoYourThingInternal()
    {
      // Lots of awesome stuff here!
    }
  }
Run Code Online (Sandbox Code Playgroud)

我不想这样做conc.DoYourThing(),因为这将调用已经单独测试的父方法.

我想只测试重写方法.

有任何想法吗?

Rog*_*ger 5

你已经标记了问题"tdd",但是当你遇到这个"问题"时我怀疑你是否遵循了这个原则.

如果你真的关注tdd你的工作流程会是这样的

  1. 为尚未实现的逻辑编写测试
  2. Impl这个测试最简单的impl使其变为绿色(例如,Conc1上的逻辑)
  3. 重构
  4. 为尚未实现的其他逻辑编写测试
  5. Impl这个测试的最简单的impl使其变为绿色(例如,Conc2上的逻辑)
  6. 重构

在"6"中,您可能认为实现模板方法是个好主意,因为Conc1和Conc2共享一些逻辑.只需这样做,并运行您的测试,看看逻辑仍然有效.

编写测试来验证逻辑,不要以实现的方式为基础(=从测试开始).在这种情况下,开始编写测试验证逻辑是否正常工作(逻辑后来放在您的具体类型中).是的,这意味着一些代码行(抽象类中的代码行)会被多次测试.但那又怎么样?编写测试的一个重点是,您应该能够重构代码,但仍然能够通过运行测试来验证它是否有效.如果您以后不想使用模板方法模式,那么在理想的世界中,您不需要更改任何测试,只需更改实现即可.

如果你开始考虑你测试的代码行,IMO你可以完全放弃编写测试的好处.您希望确保您的逻辑正常工作 - 为此编写测试.

  • (假设我们现在正在谈论TDD ......)你不只是改变代码行.您添加了新功能,这意味着添加一个或多个新测试.要使这些通过,您需要更改实现的代码.如果这意味着这会破坏旧的测试,则意味着一些旧的规范/测试不再有效.首先进行测试是件好事 - >你现在有机会知道你的新代码改变了旧的行为.新代码是否破坏了旧代码?旧的测试/规格是否不再有效?您是否需要询问您的客户/产品经理"旧规格怎么样?不再有效?" (3认同)

gui*_*e31 3

我不会考虑DoYourThingInternal()将其与(例如,可以单独测试的两个独立的代码模块)分开DoYourThing(),因为无论如何您都无法单独实例化您的抽象类,并且这两个方法将始终一起运行。此外,DoYourThingInternal()可以访问类中所有受保护的成员并可以修改它们,这对DoYourThing(). DoYourThing()所以我认为完全独立于 的具体实现进行测试是危险的DoYourThingInternal()

但是,这并不意味着您不能对DoYourThing()的预期行为(在 Abs 的所有实现中必须保持相同)和DoYourThingInternal()的预期行为进行单独的测试。

您可以使用(抽象)基测试类,在其中定义DoYourThing(). 然后创建与 Abs 实现一样多的测试子类,并针对每个实现的具体情况进行单元测试。

来自基测试类的测试将被继承,当您运行任何子类的测试时,继承的测试DoYourThing()也将运行:

public abstract class AbsBaseTest
{
  public abstract Abs GetAbs();

  [Test]
  public void TestSharedBehavior() 
  {
    getAbs().DoYourThing();

    // Test shared behavior here...
  }
}

[TestFixture]
public class AbsImplTest : AbsBaseTest
{
  public override Abs GetAbs()
  {
    return new AbsImpl();
  }

  [Test]
  public void TestParticularBehavior()
  {
    getAbs().DoYourThing();

    // Test specific behavior here
  }
}
Run Code Online (Sandbox Code Playgroud)

请参阅http://hotgazpacho.org/2010/09/testing-pattern-factory-method-on-base-test-class/

不知道所有单元测试框架是否都支持抽象测试类继承(我认为 NUnit 支持)。