单元测试在类族中的重用
我在组织基于unittest
的类测试时遇到了一些问题,特别是当我有一系列测试要做的时候。比如说,我实现了一个“字典”接口,并且有5种不同的实现想要进行测试。
我写了一个测试类来测试这个字典接口。但是,我该怎么优雅地重用这个测试类来测试我所有的实现呢?目前我做得很糟糕:
DictType = hashtable.HashDict
我在文件的顶部定义了一个DictType
,然后在测试类中使用它。要测试另一个类时,我需要手动把DictType
改成其他的东西。
有没有其他更好的方法呢?我不能给unittest
类传递参数,所以有没有更好的办法呢?
2 个回答
你可以看看 testscenarios 这个工具,它可以让你设置一个叫做场景(scenarios)的列表。然后,代码会根据列表中的每个值或场景生成一个测试类的版本。
可以参考 这个例子。
在你的情况下,场景可以是一个像这样的列表:[ {dicttype:hashtable.HashDict}, {dicttype:otherimpl.class}, ],然后在你的测试代码中使用 self.dicttype。
我处理这个问题的方法是使用标准的 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
抽象基类的模块)。