【问题标题】:Unittest from a dictionary of functions and values来自函数和值字典的单元测试
【发布时间】:2014-08-12 14:48:05
【问题描述】:

我是 Python 中 unittests 模块的新手,所以答案可能很明显。我有一堆属性要检查是否已存储在字典中:

petersen_prop = {
    "n_vertex" : 10,
    "n_edge"   : 15,
    ...
}

每个“键”都是函数的名称,值是我希望单元测试验证的目标。有效的是相当冗长的代码:

import unittest
class PetersenGraph(unittest.TestCase):

    def setUp(self):
        # Create the graph here (not important to the question)
        self.args = args
        self.adj = adj

    def test_n_vertex(self):
        self.assertTrue(n_vertex(self.adj,**self.args) == 
                        petersen_prop["n_vertex"])

    def test_n_edge(self):
        self.assertTrue(n_edge(self.adj,**self.args) == 
                        petersen_prop["n_edge"])

    # ..... hundreds of similar looking code blocks


unittest.main(verbosity=2)

似乎我违反了 DRY,我必须一遍又一遍地复制和粘贴相同的代码块。有没有办法以编程方式添加这些代码块,以便单元测试按预期工作?为了清楚起见,我希望输入是上面的字典,输出是一个工作相同的单元测试对象。

【问题讨论】:

  • n_edgen_vertex 函数定义在哪里?
  • @dano 它们在范围内是本地的,因为来自全局块 n_vertex(xxx,**xxx) 的调用将起作用。我通过from my_funcs import * 导入函数来做到这一点,这是不好的做法,但我不确定让它工作的正确方法。

标签: python unit-testing reflection dry


【解决方案1】:

您可以遍历petersen_prop字典中的键,并根据键名调用正确的函数。

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = globals()[cur_prop]
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop])

或者,如果您不将所有函数名称导入到测试模块的命名空间中,如下所示:

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = getattr(myfuncs, cur_prop)
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop])

您还可以将实际的函数对象存储在petersen_prop dict 中:

petersen_prop = {
    "n_vertex" : (10, n_vertex)
    "n_edge"   : (15, n_edge)
    ...
}

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = petersen_map[cur_prop][1]
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop][0])

编辑:

这是一种使用元类为字典中的每个项目添加唯一测试的方法:

class TestMaker(type):
    def __new__(cls, clsname, bases, dct):
        # Add a method to the class' __dict__ for every key in
        # the petersen_prop dict.
        for prop in petersen_prop:
            dct['test_{}'.format(prop)] = cls.make_test(prop)

        return super(TestMaker, cls).__new__(cls, clsname, bases, dct)

    @staticmethod
    def make_test(prop):
        # test_wrap is the method body that will be used for each
        # test
        def test_wrap(self):
            func = getattr(myfuncs, prop)
            self.assertTrue(func(self.adj, **self.args) ==
                    petersen_prop[prop])
        return test_wrap

class PetersenGraph(unittest.TestCase):
   __metaclass__ = TestMaker

运行此程序时,您将获得每个项目的单独测试用例。例如,在我的测试代码中,我的每个测试都失败了:

======================================================================
FAIL: test_n_edge (__main__.PetersenGraph)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "un.py", line 26, in test_wrap
    petersen_prop[prop])
AssertionError: False is not true

======================================================================
FAIL: test_n_vertex (__main__.PetersenGraph)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "un.py", line 26, in test_wrap
    petersen_prop[prop])
AssertionError: False is not true

【讨论】:

  • 第二种方法有效(谢谢!),但这不会只制作一个测试用例而不是多个测试用例吗? unittests 的力量难道不是能够让其他程序确定新构建中到底发生了什么错误吗?
  • @Hooked 我已经编辑了我的答案以包含一个解决方案,该解决方案为字典中的每个项目添加单独的测试。
【解决方案2】:

Python 没有从函数中获取函数名称的功能。你可以做的是test_n_vertex.__name__,它会给你'test_n_vertex'作为一个字符串。您可以使用基本的字符串函数来做您需要做的事情。但是您所期望的确切功能并不存在。

查看这里了解更多信息 Determine function name from within that function (without using traceback)

【讨论】:

  • 我不是试图解析函数的名称(我认为),我试图在现有类中创建新方法,其函数名称我已经知道并将它们添加为新测试案例。
  • 明白了。在这种情况下,您可以使用eval 将字符串转换为函数名称(例如test_n_vertex)或对象名称(例如n_vertex
  • Don't use eval。赞成我回答中的前三个选项中的任何一个。
猜你喜欢
  • 2014-07-28
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多