文章目录
转载请注明原始出处:http://blog.csdn.net/a464057216/article/details/51882991
后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
#简介#
unittest是Python的内建模块,是Python单元测试的事实标准,也叫PyUnit。使用unittest之前,先了解如下几个概念:
- test case:测试用例,可以通过创建
unitest.TestCase
类的子类创建一个测试用例。 - test fixture:包含执行测试用例前的测试准备工作、测试用例执行后的清理工作(分别对应
TestCase
中的setUp()
和tearDown()
方法),测试准备和测试清理的目的是保证每个测试用例执行前后的系统状态一致。 - test suite:测试套,是测试用例、测试套或者两者的集合,用来将有关联的测试项打包。
- test runner:负责执行测试并将结果展示给用户,可以展示图形或文字形式(
unittest.TextTestRunner
)的结果,或者返回一个错误码标识测试用例的执行结果。test runner提供了一个方法run()
,接受一个unittest.TestSuite
或unittest.TestCase
实例作为参数,执行对应测试项目后返回测试结果unittest.TestResult
对象。
#基本使用方法#
定义测试用例的方法如下:
#unit.py
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('Loo'.upper(), 'LOO')
def test_isupper(self):
self.assertTrue('LOO'.isupper())
self.assertFalse('Loo'.isupper())
def test_split(self):
s = 'Mars Loo'
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
执行脚本:
$ python unit.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
每一个测试项目的函数定义以test开头命名,这样test runner就知道哪些函数是应该被执行的。上面的例子展示了验证测试结果常用的三种方法:
assertEqual(a, b)
:比较a
==b
。assertTrue(exp)
和assertFalse(exp)
:验证bool
(exp)为True
或者False
。assertRaises(Exception)
:验证Exception
被抛出。
之所以不使用Python内建的assert抛出异常,是因为test runner需要根据这些封装后的方法抛出的异常做测试结果统计。
unittest.main()
方法会在当前模块寻找所有unittest.TestCase
的子类,并执行它们中的所有测试项目。使用-v参数可以看到更详细的测试执行过程:
$ python unit.py -v
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
也可以修改最后两行成如下代码:
suite = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
unittest.TextTestRunner(verbosity=2).run(suite)
测试结果如下:
$ python unit.py
test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
#从命令行运行unittest#
$ python -m unittest unit #直接运行模块unit中的测试用例
$ python -m unittest unit.TestStringMethods #运行模块中的某个类
$ python -m unittest unit.TestStringMethods.test_upper #运行某个单独的测试方法
混合运行测试模块、类以及测试方法也是可以的。
如果要查看unittest模块命令行的更多参数信息,使用-h
参数:
$ python -m unittest -h
-
-b
参数:只在测试用例fail或者error时显示它的stdout和stderr,否则不会显示。 -
-f
参数:如果有一个测试用例fail或者出现error,立即停止测试。 -
-c
参数:捕捉Control-C信号,并显示测试结果。
#自动发现测试用例#
unittest能够自动发现测试用例。为了让测试用例能够被自动发现,测试文件需要是在项目目录中可以import的module或者package,比如如下目录结构:
unittest
├── test_a
│ ├── __init__.py
│ └── test_a.py
└── test_b.py
在unittest目录中运行如下命令,即可运行test_a
这个package和test_b
这个module中的测试项目:
$ python -m unittest discover
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
unittest discovery默认会搜索名字命名符合test*的module或package,可以添加更多的参数:
-v
:详细输出。-s
:开始自动搜索的目录,默认是.
;这个参数也可以指向一个package名,而不是目录,例如unittest.test_a。-p
:文件匹配的模式,默认是test*.py
。-t
:项目顶级目录,默认与开始自动搜索的目录相同。
比如:
$ python -m unittest discover -s test_a
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
测试用例发现通过import模块或package执行测试,例如foo/bar/baz.py
会被import为foo.bar.baz
。
#测试代码的组织#
测试用例一定要是自包含的,即测试用例既可以独立运行,也可以和其他测试用例混合执行,测试用例执行前后不能影响系统状态。
建议将被测试代码和测试代码分离,比如一个模块module.py
对应的单元测试的文件是test_module.py
,这样方便维护。
最简单的测试用例定义,是一个unittest.TestCase
的子类只包含一个测试步骤,这个时候只需要定义一个runTest
方法,比如:
# unit.py
import unittest
class MyTestCase(unittest.TestCase):
def runTest(self):
self.assertEqual(1, 2, 'not equal')
执行测试后结果如下:
$ python -m unittest -v unit
runTest (unit.MyTestCase) ... FAIL
======================================================================
FAIL: runTest (unit.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unit.py", line 5, in runTest
self.assertEqual(1, 2, 'not equal')
AssertionError: not equal
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
如果assert*方法检查失败,会抛出一个异常,unittest会将其算作失败(failure)。任何其他异常都被unittest算作错误(error),比如:
#unit.py
import unittest
class MyTestCase(unittest.TestCase):
def runTest(self):
self.assertEqual(notexist, 2, 'not exist')
执行测试结果如下:
$ python -m unittest -v unit
runTest (unit.MyTestCase) ... ERROR
======================================================================
ERROR: runTest (unit.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unit.py", line 5, in runTest
self.assertEqual(notexist, 2, 'not exist')
NameError: global name 'notexist' is not defined
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
即failure通常是实际结果与预期结果不符,error通常是因为测试代码有bug导致。
如果很多测试项目的初始化准备工作类似,可以为他们定义同一个setUp方法,比如:
import unittest
class BaseTestCase(unittest.TestCase):
def setUp(self):
self._value = 12
class TestCase1(BaseTestCase):
def runTest(self):
self.assertEqual(self._value, 12, 'default value error')
class TestCase2(BaseTestCase):
def runTest(self):
self._value = 13
self.assertEqual(self._value, 13, 'change value fail')
如果基类BaseTestCase
的setUp
方法中抛出异常,unittest不会继续执行子类中的runTest
方法。
如果想在测试项目执行结果后进行现场清理,可以定义tearDown()
方法:
import unittest
class B(unittest.TestCase):
def setUp(self):
self._value = 1
def test_b(self):
self.assertEqual(self._value, 1)
def tearDown(self):
del self._value
setUp()
和tearDown()
方法的执行过程是:针对每一个测试项目,先执行setUp()
方法,如果成功,那么继续执行测试函数,最后不管测试函数是否执行成功,都执行tearDown()
方法;如果setUp()
方法失败,则认为这个测试项目失败,不会执行测试函数也不执行tearDown()
方法。
工作中很多测试项目依赖相同的测试夹具(setUp
和tearDown
),unittest支持像这样定义测试用例:
import unittest
class TestCase1(unittest.TestCase):
def setUp(self):
self._value = 12
def test_default(self):
self.assertEqual(self._value, 12, 'default value error')
def test_change(self):
self._value = 13
self.assertEqual(self._value, 13, 'change value fail')
如果要执行指定的测试用例的话,可以使用TestSuite这个类,包含使用方法名作为参数声明一个测试用例实例,比如:
import unittest
class TestCase1(unittest.TestCase):
def setUp(self):
self._value = 12
def test_default(self):
self.assertEqual(self._value, 12, 'default value error')
def test_change(self):
self._value = 13
self.assertEqual(self._value, 13, 'change value fail')
test_suite = unittest.TestSuite()
test_suite.addTest(TestCase1('test_default'))
test_runner = unittest.TextTestRunner()
test_runner.run(test_suite)
测试套也可以是测试套的集合,比如:
import unittest
class TestCase1(unittest.TestCase):
def setUp(self):
self._value = 12
def test_default(self):
self.assertEqual(self._value, 12, 'default value error')
def test_change(self):
self._value = 13
self.assertEqual(self._value, 13, 'change value fail')
test_suite1 = unittest.TestSuite()
test_suite1.addTest(TestCase1('test_default'))
test_suite2 = unittest.TestSuite()
test_suite2.addTest(TestCase1('test_change'))
test_suite = unittest.TestSuite([test_suite1, test_suite2])
test_runner = unittest.TextTestRunner()
test_runner.run(test_suite)
如果想执行测试类中的部分测试用例,可以采用如下方式:
def suite():
tests = ['test_default', 'test_change']
return unittest.TestSuite(map(TestCase1, tests))
test_runner = unittest.TextTestRunner()
test_runner.run(suite())
因为将一个测试用例类下面的所有测试步骤都执行一遍的情况非常普遍,unittest提
供了TestLoader
类,它的loadTestsFromTestCase()
方法会在一个TestCase
类中寻找所有以test开头的函数定义,并将他们添加到测试套中,这些函数会按照其名字的字符串排序顺序执行,比如:
import unittest
class TestCase1(unittest.TestCase):
def setUp(self):
self._value = 12
def test_default(self):
self.assertEqual(self._value, 12, 'default value error')
def test_change(self):
self._value = 13
self.assertEqual(self._value, 13, 'change value fail')
test_suite = unittest.TestLoader().loadTestsFromTestCase(TestCase1)
unittest.TextTestRunner(verbosity=2).run(test_suite)
#忽略测试用例及假设用例失败#
有些情况下需要忽略执行某些测试用例或者测试类,这个时候可以使用unittest.skip
装饰器及其变种。需要特别注意的是,可以通过skip某个测试类的setUp()
方法而跳过整个测试类的执行,比如:
import unittest, sys
version = (0, 1)
class HowToSkip(unittest.TestCase):
@unittest.skip('demonstrating skipping')
def test_nothing(self):
self.fail('will never be ran')
@unittest.skipIf(version < (1, 3),
'not supported version')
def test_format(self):
print 'your version is >= (1, 3)'
@unittest.skipUnless(sys.platform.startswith('win'),
'requires windows')
def test_winndows_support(self):
print 'support windows'
@unittest.skip('class can also be skipped')
class Skipped(unittest.TestCase):
def test_skip(self):
pass
class SkippedBySetUp(unittest.TestCase):
@unittest.skip('Skipped by setUp method')
def setUp(self):
pass
def test_dummy1(self):
print 'dummy1'
def test_dummy2(self):
print 'dummy2'
测试结果如下:
$ python -m unittest -v unit
test_format (unit4.HowToSkip) ... skipped 'not supported version'
test_nothing (unit4.HowToSkip) ... skipped 'demonstrating skipping'
test_winndows_support (unit4.HowToSkip) ... skipped 'requires windows'
test_skip (unit4.Skipped) ... skipped 'class can also be skipped'
test_dummy1 (unit4.SkippedBySetUp) ... skipped 'Skipped by setUp method'
test_dummy2 (unit4.SkippedBySetUp) ... skipped 'Skipped by setUp method'
----------------------------------------------------------------------
Ran 6 tests in 0.001s
OK (skipped=6)
特别地,被忽略的测试用例将不会执行他们的setUp()、tearDown()方法,被忽略的测试类将不会执行他们的setUpClass()、tearDownClass()方法(关于setUpClass()和tearDownClass()的详细介绍,在下一篇博客中)。
有的时候,明知道某些测试用例会失败,这时可以使用unittest.expectedFailure
装饰器,被期望失败的测试用例不会加到测试结果的failure统计中,而是加到expected failure统计中,比如:
import unittest
class ExpectedFailure(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 2, 'not equal')
测试结果如下:
$ python -m unittest -v unit
test_fail (unit.ExpectedFailure) ... expected failure
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK (expected failures=1)
如果被expectedFailure的测试用例成功了,会被加到unexpected success的计数中。
综上所述,unittest执行测试用例结束后,有6种结束状态:ok、failure、error、expected failure、skipped、unexpected success。实际工作中发送自动化测试报告时,需要注意分别这些状态的含义。
目前介绍的这些内容可以应付一些简单工作,如果想要进一步学习unittest模块的类及更多的API和结果检查方法,可以继续看我的下一篇blog。
如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!