我想编写一个类来检查集合,使用unittest.TestCase.assertEqual
展示的用于测试set
相等性的行为。它会自动打印一条好消息,说明哪些元素仅在第一组中,哪些元素仅在第二组中。
我意识到我可以实现类似的行为,但由于它已经与unittest.TestCase.assertEqual
很好地完成了,我更愿意只使用它(所以请不要回答说无益且已经很明显(但不适用于此) case)建议“不要用unittest.TestCase
”来解决这个问题。)
以下是SetChecker
类的代码:
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中使用它时,这个工作正常:
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
课。这是代码:
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__
错误:
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 unittest
源代码的目录在我的环境中是只读的,因此我无法在其中添加pdb
甚至print
语句来查看{{1}或testCaseClass
此时某些testCaseNames
失败。
但我在代码中看不到任何地方,我没有为任何__init__
方法提供所需的参数。我想知道这是否与某些幕后魔术有关,这些魔法包含从__init__
继承的类,以及我正在导入和实例化类(unittest
)的事实。要为单元测试执行的文件。
也许它会检查从SetChecker
继承的现有命名空间中的所有类?如果是这样,你如何对单元测试进行单元测试?
我还试图首先从TestCase
继承SetChecker
并试图像混合使用object
一样,但这会产生很多MRO错误并且似乎比它更令人头痛值得。
我已经尝试过搜索这个但是搜索是一个很难的错误(因为TestCase
参数似乎不是一个简单的问题)。
答案 0 :(得分:0)
我能够通过SetChecker
仅从object
继承,然后在SetChecker
内部提供继承自unittest.TestCase
的内部类来解决此问题。
问题是unittest.main
检查它运行的模块的整个命名空间。它在该模块中找到的任何继承自unittest.TestCase
的类将获得完整的测试套件处理(它将尝试为它可以找到的每个test_
方法构建类的实例,或仅用于{{ 1}}如果找不到runTest
方法)。
在我的情况下,由于set参数是必需的,无论test_
正在做什么,它都会传递一些参数(可能是函数的名称作为测试,在本例中是字符串{{ 1}})但未能传递第二个必需参数。即使这与我班级的签名一起工作(例如,假设我用两组unittest.main
替换了两个不同的参数"runTest"
和set1
,那么它会立即失败试图用该字符串进行集合操作。
似乎没有一种简单的方法可以告诉set2
忽略某个或某些类。因此,通过使tuple
只是中包含 unittest.main
的对象,SetChecker
不再找到TestCase
而不再关心。
还有一个错误:在我的unittest.main
函数中,我使用TestCase
期望可调用,但从未向test_init
类授予assertRaises
函数。< / p>
以下是修复此问题的SetChecker
类的修改:
__call__