您的位置:首页 > 编程语言 > Python开发

32.Python的单元测试工具——unittest(初级)

2016-07-11 18:11 483 查看
简介

基本使用方法

从命令行运行unittest

自动发现测试用例

测试代码的组织

忽略测试用例及假设用例失败

转载请注明原始出处: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参数:【待完善】

自动发现测试用例

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名,而不是目录

-p:文件匹配的模式,默认是
test*.py


比如:

$ python -m unittest discover -s test_a
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK


测试代码的组织

测试用例一定要是自包含的,即测试用例既可以独立运行,也可以和其他测试用例混合执行,测试用例执行前后不能影响系统状态。

建议将被测试代码和测试代码分离,比如一个模块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)


如果实际值与期望值不符,unittest将它算作失败failure,如果是代码语法有问题,将它当做错误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)


如果很多测试项目的初始化准备工作类似,可以为他们定义同一个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')


如果想在测试项目执行结果后进行现场清理,可以定义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()方法。

工作中需要测试的相似项目非常多,为了避免堆砌重复代码,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)


因为将一个测试用例类下面的所有测试步骤都执行一遍的情况非常普遍,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方法而skip整个测试类的执行,比如:

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 >=', version

@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_dummy(self):
print 'dummy'


测试结果如下:

$ python -m unittest -v unit
test_format (unit.HowToSkip) ... skipped 'not supported version'
test_nothing (unit.HowToSkip) ... skipped 'demonstrating skipping'
test_winndows_support (unit.HowToSkip) ... skipped 'requires windows'
test_skip (unit.Skipped) ... skipped 'class can also be skipped'
test_dummy (unit.SkippedBySetUp) ... skipped 'Skipped by setUp method'

----------------------------------------------------------------------
Ran 5 tests in 0.000s

OK (skipped=5)


特别地,被忽略的测试用例将不会执行他们的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模块的类及更多的API和结果检查方法,可以继续看我的下一篇blog

如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息