如何使用参数化基础 class 为 subclass 编写单元测试

How do I write unit tests for subclass with parameterized base class

鉴于我在一个名为 GBase 的程序集中有一个 class,其构造函数采用 2 个参数,GBase 的子 class(称为 GDerived)采用相同的参数,如何我将它们分开,以便我可以对 subclass?

进行单元测试

在其他程序集中:

public class GBase
{
  public GBase(ParamType1 param1, ParamType2 param2)
  {
    ...
  }

  protected ParamType1 SomeProperty { get; set; }

// other stuff
}

在这个程序集中:

public class GDerived : GBase
{
  public GDerived(ParamType1 param1, ParamType2 param2)
      :base(param1, param2)
  {
    // new code 

    SomeProperty = newCalculatedValue;

    // other stuff
  }

// other stuff
}

原始 GBase class 是遗留代码,程序的一般结构也是如此——由于代码库大小(超过 10k 行),更改结构是不可能的 - none 直到最近才为其编写单元测试。

所以现在我想为 subclass 构造函数编写一个测试(使用 NUnit)来验证正确的属性是否填充了正确的值。请注意,测试 classes 与被测试的 classes 在同一个项目中。

[TestFixture]
public class GDerivedTests
{
  [Test]
  public void GDerivedConstructor_ValidParams_PropertiesSetCorrectly()
  {
    var newGDerived = new GDerived(parameter1, parameter2);

    Assert.That(SomeProperty == parameter1;
  }
}

这是我们必须处理的一个非常粗略的表示,除了在基础 class 中设置 属性 之外,还有一些情况我们需要测试。我什至不知道从哪里开始。我有 Michael Feathers 的书 Working Effectively with Legacy Code 但它似乎没有涵盖这种普遍的 "design pattern",在我们正在处理的代码中广泛使用。是因为它是如此简单,任何眨眼的 idjyot 都应该知道如何处理它,还是因为它是一种罕见的情况?不知何故,我也不认为是,但我可能是错的...

我想到的一种可能的方法是为基础 class 提取一个接口并模拟基础 class 构造函数 - 但我不确定如何做到这一点的细节。请注意,我们都是团队单元测试的相对新手,没有经验可以借鉴。不是编码新手,只是单元测试新手。

TIA, 戴夫

首先:保持简单!在您的示例中,您唯一可以测试的是 SomeProperty。其他一切都在基础 class 中,您似乎不想测试,因此测试方法 GDerivedConstructor_ValidParams_PropertiesSetCorrectly() 没有意义。从长远来看,对其进行测试可能是明智的。

测试通常包含称为 AAA 的三个元素:安排、行动和断言。所以像这样写你的测试:

[Test]
public void GDerivedTestOfSomeProperty()
{
    // arrange
    ParamOfSomeProperty expected = ValueWeAreLookingFor; // this is something that you
                                                         // have in newCalculatedValue

    // act
    GDerived actual = new GDerived(
        AnyValueThatMakesThisTestWork1, // maybe null?
        AnyValueThatMakesThisTestWork2); // maybe null?

    // assert
    Assert.AreEqual(expected, actual.SomeProperty);
}

这就是开始。从这里走。您很快就会发现您得到了很多冗余代码,因此您可能想在一段时间后重新设计它。

模拟对于测试基础 class 或者当基础 class 对注入的对象做一些奇怪的事情时是有意义的。在这种情况下,传入模拟而不是真实对象。我个人会使用一个模拟框架来为您完成所有工作,您也可以使用它来测试基础 class 本身。一个著名的例子是 moq.

附带说明:如果将测试 classes 移到它自己的项目中,您会过得更好。出于各种原因不应发布测试代码,而且构建、测试和部署如果分开可能会更容易。