关于重构:在测试复合体上的交互时,如何避免重复的单元测试?

How do you avoid duplicate unit tests when testing interactions on composites?

想象一个过滤器系统(可能是音频过滤器或文本流过滤器)。

Filter基类有一个do_filter()方法,它接受一些输入,修改它(可能),并将其作为输出返回。

存在几个用TDD构建的子类,每个子类都有一组单独测试它们的测试。

接着出现了一个不相关类型Widget的复合类,它有两个不同Filter类型(ab的成员,它们处理相当不同的输入——也就是说,某些将由过滤器a修改的输入是通过过滤器b未修改的,反之亦然。它的process_data()方法调用每个过滤器成员的do_filter()

在开发复合类的过程中,出现了一些测试,以检查Widget的过滤器不同时处理相同数据的假设。

问题是,这些类型的测试看起来与单个过滤器的测试相同。尽管可能还有其他测试,它们的测试输入应该由两个过滤器修改,但许多测试几乎可以从每个过滤器的测试中复制和粘贴,只需进行少量修改,就可以用Widget进行测试(例如调用process_data()即可),但输入数据和断言检查是相同的。

这种复制品闻起来很难闻。但想要测试组件的交互似乎是正确的。什么样的选择可以避免这种重复?


在一个测试套件/类中有一个方法

1
2
3
4
public void TestForFooBehaviour(IFilter filter)
{
    /* whatever you would normally have in a test method */
}

然后从简单过滤器的原始测试和复合过滤器调用此方法。这也适用于抽象基类。显然,foobehaviour应该是对您正在测试的过滤器方面的有意义的描述。对您想要测试的每个行为都这样做。

如果您的语言支持duck类型或泛型,请在有帮助的情况下随意使用它。


我在这里问了一些关于抽象基类和单元测试的类似问题,其中有一些有趣的地方您可能会发现有用。

如何对抽象类进行单元测试:用存根扩展?


我经常将测试逻辑提取到单独的类中,所以我将筛选测试提取到一个单独的类中,这个类本质上不是一个单独的单元测试。尤其是如果你的测试类与你的生产代码物理上是分开的,这是一个很好的方法来解决这个问题(也就是说,没有人会认为它是生产代码,因为它在测试空间中)。