单元测试在类族中的重用

7 投票
2 回答
1669 浏览
提问于 2025-04-15 20:56

我在组织基于unittest的类测试时遇到了一些问题,特别是当我有一系列测试要做的时候。比如说,我实现了一个“字典”接口,并且有5种不同的实现想要进行测试。

我写了一个测试类来测试这个字典接口。但是,我该怎么优雅地重用这个测试类来测试我所有的实现呢?目前我做得很糟糕:

DictType = hashtable.HashDict

我在文件的顶部定义了一个DictType,然后在测试类中使用它。要测试另一个类时,我需要手动把DictType改成其他的东西。

有没有其他更好的方法呢?我不能给unittest类传递参数,所以有没有更好的办法呢?

2 个回答

0

你可以看看 testscenarios 这个工具,它可以让你设置一个叫做场景(scenarios)的列表。然后,代码会根据列表中的每个值或场景生成一个测试类的版本。

可以参考 这个例子

在你的情况下,场景可以是一个像这样的列表:[ {dicttype:hashtable.HashDict}, {dicttype:otherimpl.class}, ],然后在你的测试代码中使用 self.dicttype。

5

我处理这个问题的方法是使用标准的 unittest,通过创建子类来实现——毕竟,重写数据就像重写方法一样简单。

所以,我有一个测试的基础类:

class MappingTestBase(unittest.TestCase):
    dictype = None
    # write all the tests using self.dictype

还有一些子类:

class HashtableTest(MappingTestBase):
    dictype = hashtable.HashDict

class OtherMappingTest(MappingTestBase):
    dictype = othermodule.mappingimpl

在这里,子类只需要重写 dictype。有时候,使用“钩子方法”来暴露 MappingTestBase 也很方便。当被测试的类型在所有情况下的接口不完全相同时,可以通过让子类根据需要重写钩子方法来解决这个问题——这就是“模板方法”设计模式。你可以参考一下 这个页面,里面有我关于设计模式的一些视频讲座的评论和时间线——第二部分大约前30分钟讲的是模板方法及其变种。

当然,你不必把所有这些都放在一个模块里(虽然我发现这样布局最清晰,但你也可以为每种测试类型创建一个单独的测试模块,每个模块都 import 抽象基类的模块)。

撰写回答