首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从unittest.TestCase继承非测试功能

从unittest.TestCase继承非测试功能
EN

Stack Overflow用户
提问于 2013-11-05 16:07:58
回答 1查看 776关注 0票数 1

我想编写一个类来检查集合,使用unittest.TestCase.assertEqual为测试set等式所显示的行为。它自动打印一条很好的消息,说明哪些元素只在第一组中,哪些仅在第二组中。

我意识到我可以实现类似的行为,但是由于unittest.TestCase.assertEqual已经很好地实现了,所以我更愿意使用它(所以请不要回答那些已经很明显且已经很明显的建议“不要用unittest.TestCase解决这个问题”)。

下面是SetChecker类的代码:

代码语言:javascript
复制
import unittest
class SetChecker(unittest.TestCase):
    """
    SetChecker(set1, set2) creates a set checker from the two passed Python set
    objects. Printing the SetChecker uses unittest.TestCase.assertEqual to test
    if the sets are equal and automatically reveal the elements that are in one
    set but not the other if they are unequal. This provides an efficient way
    to detect differences in possibly large set objects. Note that this is not
    a unittest object, just a wrapper to gain access to the helpful behavior of
    unittest.TestCase.assertEqual when used on sets.
    """
    EQUAL_MSG = "The two sets are equivalent."

    def __init__(self, set1, set2, *args, **kwargs):
        assert isinstance(set1, set)
        assert isinstance(set2, set)
        super(self.__class__, self).__init__(*args, **kwargs)
        try:
            self.assertEqual(set1, set2)
            self._str = self.EQUAL_MSG
            self._str_lines = [self._str]
            self._indxs = None
        except AssertionError, e:
            self._str = str(e)
            self._str_lines = self._str.split('\n')

            # Find the locations where a line starts with 'Items '.
            # This is the fixed behavior of unittest.TestCase.
            self._indxs = [i for i,y in enumerate(self._str_lines) 
                           if y.startswith('Items ')]

    def __repr__(self):
        """
        Convert SetChecker object into a string to be printed.
        """
        return self._str

    __str__ = __repr__ # Ensure that `print` and __repr__ do the same thing.

    def runTest(self):
        """
        Required by any sub-class of unittest.TestCase. Solely used to inherit
        from TestCase and is not implemented for any behavior.
        """
        pass

    def in_first_set_only(self):
        """
        Return a list of the items reported to exist only in the first set. If
        the sets are equivalent, returns a string saying so.
        """
        return (set(self._str_lines[1:self._indxs[1]]) 
                if self._indxs is not None else self.EQUAL_MSG)

    def in_second_set_only(self):
        """
        Return a list of the items reported to exist only in the second set. If
        the sets are equivalent, returns a string saying so.
        """
        return (set(self._str_lines[1+self._indxs[1]:]) 
                if self._indxs is not None else self.EQUAL_MSG)

当我在IPython中使用它时,它工作得很好:

代码语言:javascript
复制
In [1]: from util.SetChecker import SetChecker

In [2]: sc = SetChecker(set([1,2,3, 'a']), set([2,3,4, 'b']))

In [3]: sc
Out[3]:
Items in the first set but not the second:
'a'
1
Items in the second set but not the first:
'b'
4

In [4]: print sc
Items in the first set but not the second:
'a'
1
Items in the second set but not the first:
'b'
4

In [5]: sc.in_first_set_only()
Out[5]: set(["'a'", '1'])

In [6]: sc.in_second_set_only()
Out[6]: set(["'b'", '4'])

但是现在我也想为这个类编写单元测试。所以我做了一个TestSetChecker类。这是代码:

代码语言:javascript
复制
from util.SetChecker import SetChecker
class TestSetChecker(unittest.TestCase):
    """
    Test class for providing efficient comparison and printing of
    the difference between to sets.
    """
    def setUp(self):
        """
        Create examples for testing.
        """
        self.set1 = set([1, 2, 3, 'a'])
        self.set2 = set([2, 3, 4, 'b'])
        self.set3 = set([1,2])
        self.set4 = set([1,2])
        self.bad_arg = [1,2]
        self.expected_first = set(['1', 'a'])
        self.expected_second = set(['4', 'b'])
        self.expected_equal_message = SetChecker.EQUAL_MSG
        self.expected_print_string = (
            "Items in the first set but not the second:\n'a'\n1\n"
            "Items in the second set but not the first:\n'b'\n4")

    def test_init(self):
        """
        Test constructor, assertions on args, and that instance is of proper
        type and has expected attrs.
        """
        s = SetChecker(self.set1, self.set2)
        self.assertIsInstance(s, SetChecker)
        self.assertTrue(hasattr(s, "_str")) 
        self.assertTrue(hasattr(s, "_str_lines"))
        self.assertTrue(hasattr(s, "_indxs"))
        self.assertEqual(s.__repr__, s.__str__)
        self.assertRaises(AssertionError, s, *(self.bad_arg, self.set1))

    def test_repr(self):
        """
        Test that self-printing is correct.
        """
        s1 = SetChecker(self.set1, self.set2)
        s2 = SetChecker(self.set3, self.set4)
        self.assertEqual(str(s1), self.expected_print_string)
        self.assertEqual(str(s2), self.expected_equal_message)

    def test_print(self):
        """
        Test that calling `print` on SetChecker is correct.
        """
        s1 = SetChecker(self.set1, self.set2)
        s2 = SetChecker(self.set3, self.set4)
        s1_print_output = s1.__str__()
        s2_print_output = s2.__str__()
        self.assertEqual(s1_print_output, self.expected_print_string)
        self.assertEqual(s2_print_output, self.expected_equal_message)

    def test_in_first_set_only(self):
        """
        Test that method gives list of set elements found only in first set.
        """
        s1 = SetChecker(self.set1, self.set2)
        s2 = SetChecker(self.set3, self.set4)
        fs1 = s1.in_first_set_only()
        fs2 = s2.in_first_set_only()
        self.assertEqual(fs1, self.expected_first)
        self.assertEqual(fs2, self.expected_equal_message)

    def test_in_second_set_only(self):
        """
        Test that method gives list of set elements found only in second set.
        """
        s1 = SetChecker(self.set1, self.set2)
        s2 = SetChecker(self.set3, self.set4)
        ss1 = s1.in_second_set_only()
        ss2 = s2.in_second_set_only()
        self.assertEqual(ss1, self.expected_second)
        self.assertEqual(ss2, self.expected_equal_message)        

if __name__ == "__main__":
    unittest.main()

据我所知,TestSetChecker与我编写的许多其他单元测试类没有区别(除了它正在测试的特定功能)。

然而,当我试图执行包含单元测试的文件时,我看到了一个非常不寻常的__init__错误:

代码语言:javascript
复制
EMS@computer ~/project_dir/test $ python TestSetChecker.py
Traceback (most recent call last):
  File "TestSetChecker.py", line 84, in <module>
    unittest.main()
  File "/opt/python2.7/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/opt/python2.7/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/opt/python2.7/lib/python2.7/unittest/main.py", line 155, in createTests
    self.test = self.testLoader.loadTestsFromModule(self.module)
  File "/opt/python2.7/lib/python2.7/unittest/loader.py", line 65, in loadTestsFromModule
    tests.append(self.loadTestsFromTestCase(obj))
  File "/opt/python2.7/lib/python2.7/unittest/loader.py", line 56, in loadTestsFromTestCase
    loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
TypeError: __init__() takes at least 3 arguments (2 given)

在我的环境中,带有Python源代码的目录是只读的,所以我不能在那里添加pdb或甚至print语句来查看某些__init__失败时的testCaseClasstestCaseNames是什么。

但是,我看不到代码中有什么地方不能为任何__init__方法提供所需的参数。我想知道这是否与继承自unittest的类的幕后魔法有关,是否与我在要为单元测试执行的文件中导入和实例化一个类(SetChecker)有关。

也许它检查从TestCase继承的现有命名空间中的所有类?如果是的话,你是如何测试单元测试的?

我还试图首先让SetChecker继承object,并尝试将TestCase作为一种混合使用,但这造成了许多MRO错误,似乎比它更让人头疼。

我已经尝试过搜索它,但是搜索是一个很困难的错误(因为__init__参数似乎不是一个简单的问题)。

EN

回答 1

Stack Overflow用户

发布于 2013-11-05 17:16:43

通过让SetChecker只从object继承,然后在SetChecker内部提供继承自unittest.TestCase的内部类,我就能够解决这个问题。

问题是,unittest.main检查它运行的模块的整个命名空间。它在该模块中从unittest.TestCase继承的任何类都将得到完整的测试套件处理(它将尝试为它可以找到的每个test_方法构造类的实例,如果没有找到test_方法,则只为runTest构造实例)。

在我的例子中,由于set参数是必需的,无论unittest.main在做什么,它都传递一些参数(可能是要作为测试处理的函数的名称,在本例中是字符串"runTest"),但是没有传递第二个必需的参数。即使这与类的签名(例如,假设我将两个不同的参数set1set2替换为2集的tuple )起作用,一旦它尝试使用该字符串执行set操作,它也会立即失败。

似乎没有一种简单的方法可以让unittest.main忽略某个或多个类。因此,通过使SetChecker仅仅是一个在其中有一个TestCase的对象,unittest.main不再发现TestCase而不再关心它。

还有一个错误:在我的test_init函数中,我使用了assertRaises,它需要一个可调用的,但从来没有给过我的SetChecker类一个__call__函数。

下面是对SetChecker类的修改,它为我修复了这个问题:

代码语言:javascript
复制
class SetChecker(object):
    """
    SetChecker(set1, set2) creates a set checker from the two passed Python set
    objects. Printing the SetChecker uses unittest.TestCase.assertEqual to test
    if the sets are equal and automatically reveal the elements that are in one
    set but not the other if they are unequal. This provides an efficient way
    to detect differences in possibly large set objects. Note that this is not
    a unittest object, just a wrapper to gain access to the helpful behavior of
    unittest.TestCase.assertEqual when used on sets.
    """
    EQUAL_MSG = "The two sets are equivalent."

    class InternalTest(unittest.TestCase):
        def runTest(self): pass

    def __init__(self, set1, set2):
        assert isinstance(set1, set)
        assert isinstance(set2, set)
        self.int_test = SetChecker.InternalTest()

        try:
            self.int_test.assertEqual(set1, set2)
            self._str = self.EQUAL_MSG
            self._str_lines = [self._str]
            self._indxs = None
        except AssertionError, e:
            self._str = str(e)
            self._str_lines = self._str.split('\n')

            # Find the locations where a line starts with 'Items '.
            # This is the fixed behavior of unittest.TestCase.
            self._indxs = [i for i,y in enumerate(self._str_lines) 
                           if y.startswith('Items ')]
    @classmethod
    def __call__(klass, *args, **kwargs):
        """
        Makes the class callable such that calling it like a function is the
        same as constructing a new instance.
        """
        return klass(*args, **kwargs)

    # Everything else below is the same...
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19793434

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档