最近做项目要用到测试防止项目代码的更改导致以前的功能受到影响,所以初步了解了一下python 中的unittest。以下是简要的备忘笔记。
主要参考 unittest官方文档
python提供了很多工具用来进行测试,unittest就是最常用的之一,以下是unittest使用的一个简单的例子:
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
其中,设置一个子类继承TestCase类。在该类中定义了三个不同的测试方法。名字都以test开头。这样,以后判断测试用例的时候便可以将这些方法都调入到test suite中去。
其中,除了判断布尔型是否一致的assertTrue以及assertFalse,还有判断是否会raise一场的assertRaises。可以用这类assert方法进行不同更多测试。执行main之后就会依次执行这些测试用例。执行的顺序是按照这些方法的字符串字母顺序进行执行的。
再来看一段代码:
import unittest
class DefaultWidgetSizeTestCase(unittest.TestCase):
def test_default_widget_size(self):
widget = Widget('The widget')
self.assertEqual(widget.size(), (50, 50))
在这段代码中,先初始化一个widget,再对其进行相应的测试,有个缺点就是,如果有多种不同的测试用例,那么每次都得初始化,这里,unittest提供了一个统一初始化环境的方式,即使用setUp方法,代码如下:
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def test_default_widget_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_widget_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
这里的setUp中提前初始化需要的环境,然后其他测试方法直接在该环境中执行即可。测试的时候,程序会首先根据setUp来初始化环境再进行测试,如果setUp自己本身raise了一异常,那么整个测试就会报错,测试方法不会执行。
类似的,测试完之后可能需要恢复环境到最初的状态,可以使用tearDown方法来实现:
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
这种工作环境的创建与销毁被成为fixture。
测试用例被test suite组合在一起,大多数情况下,调用unittest.mian()就会手机所有模块的测试用例并执行他们。
同时,也可以自己来构建test suite:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_widget_size'))
suite.addTest(WidgetTestCase('test_widget_resize'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
这里定义了一个TestSuite,并且将那几个以test开头的测试方法都实例化为测试用例加入到suite之中。最后构建一个测试的TextTestRunner,并用该runner执行相应的suite即可。
在以上所有的例子中,每个测试中间都是执行一次测试,并没有循环迭代进行测试,如果需要循环测试,就要用到subTest这个方法:
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
这样,测试之后的输出就会如下:
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
而如果不适用subTest直接使用循环内assertEqual,程序就会在遇到第一个测试错误时结束测试。并且也不能显示i的值。
======================================================================
FAIL: test_even (__main__.NumbersTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
以上就是主要的关于unittest的用法。