Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的单元测试有最小的样式吗?_Python_Unit Testing - Fatal编程技术网

Python中的单元测试有最小的样式吗?

Python中的单元测试有最小的样式吗?,python,unit-testing,Python,Unit Testing,我想知道人们使用什么技术来简化用于单元测试的代码的“大小”。例如,我试图封送类的一个对象并测试封送对象(但这假定封送工作正常) 以班级为例 import unittest class Nums(object): def __init__(self, n1_, n2_, n3_): self.n1, self.n2, self.n3 = n1_, n2_, n3_ def marshal(self): return "n1 %g, n2 %g, n3 %g"%(se

我想知道人们使用什么技术来简化用于单元测试的代码的“大小”。例如,我试图封送类的一个对象并测试封送对象(但这假定封送工作正常)

以班级为例

import unittest
class Nums(object):
    def __init__(self, n1_, n2_, n3_):
        self.n1, self.n2, self.n3 = n1_, n2_, n3_
def marshal(self):
    return "n1 %g, n2 %g, n3 %g"%(self.n1,self.n2,self.n3)
然后是基于封送、基于列表和正常的测试

class NumsTests(unittest.TestCase):
    def setUp(self):
        self.nu = Nums(10,20,30)
    def test_init1(self):
        self.assertEquals(self.nu.marshal(),"n1 %g, n2 %g, n3 %g"%(10,20,30))
    def test_init2(self):
        self.assertEquals([self.nu.n1,self.nu.n2,self.nu.n3],[10,21,31])
    def test_init3(self):
        self.assertEquals(self.nu.n1,10)
        self.assertEquals(self.nu.n2,21)
        self.assertEquals(self.nu.n3,31)
这会导致以下错误(因为,20!=21和30!=31,我的测试初始化不好或测试条件错误)

第一个和第二个错误消息很难理解(因为您必须解析字符串或列表)。然而,第三种技术在用于测试复杂对象的代码数量上迅速爆炸

有没有更好的方法来简化单元测试而不产生困难的错误消息?而且,不依赖封送处理函数的准确性


[将
测试封送
更改为
封送
]

我赞同上面的评论,即您不应该对正在测试的实际类使用测试方法。像
test\u marshal
这样的函数应该放在其他地方(假设它们确实存在用于测试而不是用于一般用途),通常放在单元测试文件中。然而,暂时把它放在一边,我会这样做

import unittest

class Nums(object):
    FORMAT = "n1 %g, n2 %g, n3 %g"  # make this a variable for easy testing

    def __init__(self, n1, n2, n3):
        self.n1, self.n2, self.n3 = n1, n2, n3  # no underscores necessary

    def test_marshal(self):
        return self.FORMAT % (self.n1, self.n2, self.n3)


class NumsTests(unittest.TestCase):
    def setUp(self):
        self.nums = [10, 20, 30]    # make a param list variable to avoid duplication
        self.nu = Nums(*self.nums)  # Python "apply" syntax
        self.nu_nums = [self.nu.n1, self.nu.n2, self.nu.n3]  # we'll use this repeatedly

    def test_init1(self):
        self.assertEquals(self.nu.test_marshal(), self.nu.FORMAT % self.nums )

    def test_init2(self):
        self.assertEquals(self.nums, self.nu_nums)

    def test_init3(self):
        for reference, test in zip(self.nums, self.nu_nums):
            self.assertEquals(reference, test)
有关上述应用语法的说明,请参阅

通过将要测试的内容放入变量中,可以避免代码重复,这似乎是您最关心的问题

至于令人困惑的错误消息,我想这取决于您觉得需要多少细节。就个人而言,我的单元测试为我提供了一行预期的、不存在的代码和值,这一事实倾向于让我相当清楚出了什么问题。但是,如果您确实想要一些东西明确告诉您哪个字段是不正确的,而不是那些不匹配的值,并且您想要避免代码重复,那么您可以编写如下内容:

class NumsTests(unittest.TestCase):
    def setUp(self):
        self.nums = {"n1": 10, "n2": 20, "n3": 30}  # use a dict, not a list
        self.nu = Nums(**self.nums)                 # same Python "apply" syntax

    # test_init1 and test_init2 omitted for space

    def test_init3(self):
        for attr,val in self.nums.items():
            self.assertEqual([attr, val], [attr, getattr(self.nu, val)])
如果您曾经有过不匹配的值,那么现在会出现如下错误

AssertionError: ["n1", 10] != ["n1", 11]
因此,您可以一眼就知道哪个字段不匹配,而不必根据值来进行推理。这种方法仍然保留了代码扩展问题,因为无论向
Nums
类添加多少参数,test_init3都将保持相同的大小


请注意,这种使用要求您的
\uuuu init\uuu
参数与num类中的字段具有相同的名称,例如,它们必须命名为
n1
,而不是
n1\u
,等等。另一种方法是使用。

进行初始化测试,我建议不要通过调用
封送()
功能。然后,您不仅需要分析哪个初始值设定项失败,还需要确定是您的初始化失败还是封送处理函数本身失败。我推荐的单元测试的“最小风格”是尽可能缩小在任何测试点测试的焦点

如果我真的要测试一大堆字段的初始化,我可能会像Eli一样进行重构:

class MyTestCase(unittest.TestCase):
    def assertFieldsEqual(self, obj, expectedFieldValDict):
        for fieldName, fieldVal in expectedFieldValDict.items():
            self.assertFieldEqual(obj, fieldName, fieldVal)
    def assertFieldEqual(self, obj, fieldName, expectedFieldVal):
        actualFieldVal = getattr(obj, fieldName)
        if expectedFieldVal != actualFieldVal:
            msg = "Field %r doesn't match: expected %r, actual %r" % \
                (fieldName, expectedFieldVal, actualFieldVal)
            self.fail(msg)

class NumsTests(MyTestCase):
    def setUp(self):
        self.initFields = {'n1': 10, 'n2': 20, 'n3': 30}
        self.nums = Nums(**initFields)
    def test_init(self):
        self.assertFieldsEqual(self.nums, self.initFields)
“好悲伤,”我能听到你说,“这是很多代码!”是的,但区别在于:

  • assertFieldsEqual
    assertFieldEqual
    是可重用的函数,它们被抽象为一个公共测试用例类,您的其他测试用例可以重用
  • 故障消息准确地描述了发生的情况
当需要测试封送处理函数时,您可以简单地执行以下操作:

def test_marshal(self):
    expected = '...'
    self.assertEqual(expected, self.nums.marshal())

不过,在比较字符串时,我更喜欢一种方法,它能准确地告诉我字符串的发散位置。对于多行字符串,Python2.7中现在有了一个方法,但我过去为此目的滚动或抄袭了自己的方法。

您的系统中不应该有任何测试代码(在本例中是
Nums
类)。@Skilldrick我不知道“您的系统中的测试代码”是什么平均值。测试
Nums
的代码应与类
Nums
本身位于单独的文件中。因此,将
test\u marshal
移动到另一个文件(即您的测试文件)。好的,我明白您的意思-谢谢。实际上,我应该将其命名为
marshal
,因为我确实希望能够将对象作为文本转储以用于其他目的。或者正如下面提到的
\uuuu str\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?或者将其重命名为uuu repr_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。
def test_marshal(self):
    expected = '...'
    self.assertEqual(expected, self.nums.marshal())