7

假设以下模板方法实现:

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();

  }

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

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

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

我只想测试覆盖方法。

有任何想法吗?

4

5 回答 5

5

您已将问题标记为“tdd”,但我怀疑您在遇到此“问题”时是否遵循了该原则。

如果您真正遵循 tdd,您的工作流程将类似于

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

在“6”中,您可能认为实现模板方法是个好主意,因为 Conc1 和 Conc2 共享一些逻辑。只需执行此操作,然后运行测试以查看逻辑是否仍然有效。

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

如果您开始考虑要测试哪些代码行,IMO 您将失去编写测试的大部分好处。您想确保您的逻辑有效 - 而是为此编写测试。

于 2012-09-25T17:38:24.413 回答
3

我认为“问题”的一部分是无法从类外部调用受保护的方法。Conc一个派生自并提供新公共方法的模拟类怎么样:

public class MockConc: Conc
  {
    void MockYourThingInternal()
    {
      DoYourThingInternal()
    }
  }
于 2012-09-24T17:45:30.200 回答
2

我不会考虑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
  }
}

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

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

于 2012-09-26T15:38:48.690 回答
0

在 Abs 上粘贴一个界面并模拟它怎么样?忽略电话,或对他们设定期望?

于 2012-09-24T16:48:40.637 回答
0

您可以通过多种方式进行操作,其中许多已在此处记录。这是我通常采用的方法:让测试用例继承自具体类。

public ConcTest : Conc
{
    [Test]
    public void DoesItsThingRight()
    {
         var Result = DoItsThingInternal();
         // Assert the response
    }
}

希望有帮助!

布兰登

于 2012-09-27T03:22:11.907 回答