Python单元测试的最佳做法-多个功能适用于同一对象

时间:2019-03-17 15:27:48

标签: python unit-testing testing

我有一堆应用于相似对象的函数,例如代表n维盒子的Numpy数组:

# 3-D box parameterized as:
#     box[0] = 3-D min coordinate
#     box[1] = 3-D max coordinate
box = np.array([
    [1, 3, 0],
    [4, 5, 7]
])

现在,我有很多功能要在盒子列表上运行,例如。 volumesintersectionsmallest_containing_box等。在我看来,这是我希望设置的方式:

# list of test functions:
test_funcs = [volume, intersection, smallest_containing_box, ...]
# manually create a bunch of inputs and outputs
test_set_1 = (
    input = [boxA, boxB, ...], # where each of these are np.Array objects
    output = [
        [volA, volB, ...], # floats I calculated manually
        intersection, # np.Array representing the correct intersection
        smallest_containing_box, # etc.
    ]
)
# Create a bunch of these, eg. test_set_2, test_set_3, etc. and bundle them in a list:
test_sets = [test_set_1, ...]
# Now run the set of tests over each of these:
test_results = [[assertEqual(test(t.input), t.output) for test in test_funcs] for t in test_sets]

我要以这种方式进行构造的原因是,我可以创建多组(输入,答案)对,并仅对每对进行所有测试。除非我丢失了某些内容,否则unittest的结构似乎不适用于这种方法。相反,似乎要我为每对函数和输入(即

)创建一个单独的TestCase对象。
class TestCase1(unittest.TestCase):
    def setUp(self):
        self.input = [...]
        self.volume = [volA, volB, ...]
        self.intersection = ...
        # etc.

    def test_volume(self):
        self.assertEqual(volume(self.input), self.volume)

    def test_intersection(self):
        self.assertEqual(intersection(self.input), self.output)

    # etc.

# Repeat this for every test case!?

这似乎是疯狂的样板。我想念什么吗?

2 个答案:

答案 0 :(得分:0)

让我们尝试描述我如何理解您的方法:您实现了许多具有相似性的不同功能,即它们对相同类型的输入数据进行操作。在测试中,您尝试利用这种相似性:创建一些输入数据并将该输入数据传递给所有函数。

这种以测试数据为中心的方法是不寻常的。典型的单元测试方法是以代码为中心的。原因是,单元测试的一个主要目标是发现代码中的错误。不同的功能(显然)具有不同的代码,因此,错误的类型可能不同。因此,通常精心设计测试数据以识别相应代码中的某些类型的错误。测试设计方法是有条理地设计测试用例的方法,理想情况下可以检测到所有可能的错误。

我怀疑,以您的测试数据为中心的方法,您将同样成功地找到不同功能中的错误:对于volume功能,可能会有一些不存在的上溢情况(以及下溢情况)。 t申请intersectionsmallest_containing_box。相比之下,将必须有空的相交,单点相交等。因此,似乎每个功能可能都需要专门设计的测试方案。

关于似乎是以代码为中心的单元测试的结果的样板代码:有几种方法可以限制这种情况。您将同意针对不同的被测功能使用不同的测试方法。但是您可以使用参数化测试来避免进一步的代码重复。并且,对于仍然可以看到(至少有时)对不同功能使用通用测试数据的情况:对于这种情况,可以使用创建测试数据的工厂函数,并可以从不同测试用例中调用。例如,您可能有一个工厂函数make-unit-cube,可以在不同的测试中使用。

答案 1 :(得分:0)

尝试unittest.TestSuite()。这为您提供了一个可以在其中添加测试用例的对象。在您的情况下,请创建套件,然后遍历您的列表,创建TestCase的实例,这些实例都只有一个测试方法。将测试数据传递给构造函数,然后将其保存到那里的属性中,而不是放在setUp()中。

当您使用名为suite()的方法创建套件并运行所有套件时,单元测试运行器将检测到套件。

注意:为每个TestCase实例分配一个名称,否则很难找出哪个失败。