PyUnit(unittest) 是 Python 自带的单元测试框架,用于编写和运行可重复的测试。PyUnit 是 xUnit 体系的一个成员,xUnit 是众多测试框架的总称,PyUnit 主要用于进行白盒测试和回归测试。
unittest核心概念:
所有测试的本质其实都是一样的,都是通过给定参数来执行函数,然后判断函数的实际输出结果和期望输出结果是否一致。PyUnit 测试与其他 xUnit 的套路一样,基于断言机制来判断函数或方法的实际输出结果和期望输出结果是否一致,测试用例提供参数来执行函数或方法,获取它们的执行结果,然后使用断言方法来判断该函数或方法的输出结果与期望输出结果是否一致,如果一致则说明测试通过;如果不一致则说明测试不通过。
断言方法 | 断言描述 |
---|---|
assertEqual(arg1, arg2, msg=None) | 验证arg1=arg2,不等则fail |
assertNotEqual(arg1, arg2, msg=None) | 验证arg1 != arg2, 相等则fail |
assertTrue(expr, msg=None) | 验证expr是true,如果为false,则fail |
assertFalse(expr,msg=None) | 验证expr是false,如果为true,则fail |
assertIs(arg1, arg2, msg=None) | 验证arg1、arg2是同一个对象,不是则fail |
assertIsNot(arg1, arg2, msg=None) | 验证arg1、arg2不是同一个对象,是则fail |
assertIsNone(expr, msg=None) | 验证expr是None,不是则fail |
assertIsNotNone(expr, msg=None) | 验证expr不是None,是则fail |
assertIn(arg1, arg2, msg=None) | 验证arg1是arg2的子串,不是则fail |
assertNotIn(arg1, arg2, msg=None) | 验证arg1不是arg2的子串,是则fail |
assertIsInstance(obj, cls, msg=None) | 验证obj是cls的实例,不是则fail |
assertNotIsInstance(obj, cls, msg=None) | 验证obj不是cls的实例,是则fail |
代码示例:
import unittest
import sys
class demoTest(unittest.TestCase):
def test1(self):
self.assertEqual(4 + 5,9)
def test2(self):
self.assertNotEqual(5 * 2,10)
def test3(self):
self.assertTrue(4 + 5 == 9,"The result is False")
def test4(self):
self.assertTrue(4 + 5 == 10,"assertion fails")
def test5(self):
self.assertIn(3,[1,2,3])
def test6(self):
self.assertNotIn(3, range(5))
unittest.main()
结果:
E:\pythonCode>python sayhello.py
.F.F.F
======================================================
FAIL: test2 (__main__.demoTest)
------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 8, in test2
self.assertNotEqual(5 * 2,10)
AssertionError: 10 == 10
======================================================
FAIL: test4 (__main__.demoTest)
------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 14, in test4
self.assertTrue(4 + 5 == 10,"assertion fails")
AssertionError: False is not true : assertion fails
======================================================
FAIL: test6 (__main__.demoTest)
------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 20, in test6
self.assertNotIn(3, range(5))
AssertionError: 3 unexpectedly found in range(0, 5)
------------------------------------------------------
Ran 6 tests in 0.003s
FAILED (failures=3)
断言方法 | 断言描述 |
---|---|
assertAlmostEqual (first, second, places = 7, msg = None, delta = None) | 验证first约等于second。 palces: 指定精确到小数点后多少位,默认为7 |
assertNotAlmostEqual (first, second, places, msg, delta) | 验证first不约等于second。 palces: 指定精确到小数点后多少位,默认为7 |
aassertGreater (first, second, msg = None) | 验证first > second,否则fail |
assertGreaterEqual (first, second, msg = None) | 验证first ≥ second,否则fail |
assertLess (first, second, msg = None) | 验证first < second,否则fail |
assertLessEqual (first, second, msg = None) | 验证first ≤ second,否则fail |
assertRegexpMatches (text, regexp, msg = None) | 验证正则表达式regexp搜索匹配的文本text。 regexp:通常使用re.search() |
assertNotRegexpMatches (text, regexp, msg = None) | 验证正则表达式regexp搜索不匹配的文本text。 regexp:通常使用re.search() |
注: 在上述的两个函数中,如果delta指定了值,则first和second之间的差值必须≤delt
代码示例:
import unittest
import math
import sys
class demoTest(unittest.TestCase):
def test1(self):
self.assertAlmostEqual(22.0/7,3.14)
def test2(self):
self.assertNotAlmostEqual(10.0/3,3)
def test3(self):
self.assertGreater(math.pi,3)
def test4(self):
self.assertNotRegexpMatches("Tutorials Point (I) Private Limited",
"Point")
unittest.main()
结果:
E:\pythonCode>python sayhello.py
F..sayhello.py:42: DeprecationWarning: Please use assertNotRegex instead.
"Point")
F
======================================================================
FAIL: test1 (__main__.demoTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 32, in test1
self.assertAlmostEqual(22.0/7,3.14)
AssertionError: 3.142857142857143 != 3.14 within 7 places (0.0028571428571426694
difference)
======================================================================
FAIL: test4 (__main__.demoTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 42, in test4
"Point")
AssertionError: Regex matched: 'Point' matches 'Point' in 'Tutorials Point (I) P
rivate Limited'
----------------------------------------------------------------------
Ran 4 tests in 0.006s
FAILED (failures=2)
断言方法 | 断言描述 |
---|---|
assertListEqual (list1, list2, msg = None) | 验证列表list1、list2相等,不等则fail,同时报错信息返回具体的不同的地方 |
assertTupleEqual (tuple1, tuple2, msg = None) | 验证元组tuple1、tuple2相等,不等则fail,同时报错信息返回具体的不同的地方 |
assertSetEqual (set1, set2, msg = None) | 验证集合set1、set2相等,不等则fail,同时报错信息返回具体的不同的地方 |
assertDictEqual (expected, actual, msg = None) | 验证字典expected、actual相等,不等则fail,同时报错信息返回具体的不同的地方 |
代码示例:
import unittest
import sys
class demoTest(unittest.TestCase):
def test1(self):
self.assertListEqual([2,3,4], [1,2,3,4,5])
def test2(self):
self.assertTupleEqual((1*2,2*2,3*2), (2,4,6))
def test3(self):
self.assertDictEqual({1:11,2:22},{3:33,2:22,1:11})
unittest.main()
结果:
E:\pythonCode>python sayhello.py
F.F
=======================================================
FAIL: test1 (__main__.demoTest)
-------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 50, in test1
self.assertListEqual([2,3,4], [1,2,3,4,5])
AssertionError: Lists differ: [2, 3, 4] != [1, 2, 3, 4,
First differing element 0:
2
1
Second list contains 2 additional elements.
First extra element 3:
4
- [2, 3, 4]
+ [1, 2, 3, 4, 5]
? +++ +++
=======================================================
FAIL: test3 (__main__.demoTest)
-------------------------------------------------------
Traceback (most recent call last):
File "sayhello.py", line 57, in test3
self.assertDictEqual({1:11,2:22},{3:33,2:22,1:11})
AssertionError: {1: 11, 2: 22} != {3: 33, 2: 22, 1: 11}
- {1: 11, 2: 22}
+ {1: 11, 2: 22, 3: 33}
? +++++++
-------------------------------------------------------
Ran 3 tests in 0.004s
FAILED (failures=2)
在PyCharm中建立项目,工程列表如下图:
其中的report.html和report.txt文件是运行生成的测试结果文件
各部分代码,代码中包含操作注释:
#mathfunc.py
#待测的方法
def add(a,b):
return a+b
def minus(a,b):
return a-b
def multi(a,b):
return a*b
def divide(a,b):
return a/b
#针对mathfunc.py的测试用例
#test_mathfunc.py
import unittest
from test import mathfunc
class TestMathFunc(unittest.TestCase):# 继承unittest.TestCase
"""Test mathFunc.py"""
def test_add(self):
"""Test method add(a,b)"""
self.assertEqual(3,mathfunc.add(1,2))
self.assertNotEqual(3,mathfunc.add(2,2))
def test_minus(self):
"""Test method minus(a,b)"""
self.assertEqual(1,mathfunc.minus(3,2))
def test_multi(self):
"""Test method multi(a,b)"""
self.assertEqual(6,mathfunc.multi(3,2))
def test_divide(self):
"""Test method divide(a,b)"""
self.assertEqual(2,mathfunc.divide(6,3))
self.assertEqual(2.5,mathfunc.divide(5,2))
#与test_mathfunc.py的区别主要是演示setUpClass()与tearDownClass()和skipTest相关操作
#test_mathfunc2.py
import unittest
from test import mathfunc
class TestMathFunc2(unittest.TestCase):
#如果想要在所有case执行之前准备一次环境,并在所有case执行结束后再清理环境,我们可以用setUpClass()与tearDownClass(),注意:@classmethod必须加,否则报错
@classmethod
def setUpClass(cls):
print ('This setUpClass() method only called once')
@classmethod
def tearDownClass(cls):
print ('This tearDownClass() method only called once too')
#@unittest.skipTest('test_add')
def test_add(self):
"""Test method add(a,b)"""
self.assertEqual(3, mathfunc.add(1, 2))
self.assertNotEqual(3, mathfunc.add(2, 2))
def test_minus(self):
"""Test method minus(a,b)"""
self.assertEqual(1, mathfunc.minus(3, 2))
def test_multi(self):
"""Test method multi(a,b)"""
self.assertEqual(6, mathfunc.multi(3, 2))
def test_divide(self):
"""Test method divide(a,b)"""
self.assertEqual(2, mathfunc.divide(6, 3))
self.assertEqual(2.5, mathfunc.divide(5, 2))
#整个测试流程从加载测试用例到生成测试报告
#suit_mathfunc.py
import unittest
from test.test_mathfunc import TestMathFunc
from test.test_mathfunc2 import TestMathFunc2
from HTMLTestRunner import HTMLTestRunner
'''
#直接用addTests方法添加TestCase列表,可以确定case的执行顺序
suite=unittest.TestSuite ()
tests=[TestMathFunc('test_add'),TestMathFunc('test_minus'),TestMathFunc('test_multi'),TestMathFunc('test_divide')]
suite.addTests(tests)
runner=unittest.TextTestRunner(verbosity= 2)
runner.run(suite)
'''
'''
#直接用addTest方法添加单个TestCase
suite=unittest.TestSuite ()
tests=(TestMathFunc('test_add'))
suite.addTest(tests)
runner=unittest.TextTestRunner(verbosity= 2)
runner.run(suite)
'''
'''
#直接用addTests方法+TestLoader
suite=unittest.TestSuite ()#创建测试用例
loader=unittest.TestLoader()#加载测试用例
suite.addTests(loader.loadTestsFromName('test_mathfunc.TestMathFunc'))
runner=unittest.TextTestRunner(verbosity= 2)
runner.run(suite)
'''
#用addTests方法+TestLoader+loadTestsFromName后加列表,可测试多个模并确定执行顺序
suite=unittest.TestSuite ()#创建测试用例
loader=unittest.TestLoader()#加载测试用例
suite.addTests(loader.loadTestsFromNames(['test_mathfunc.TestMathFunc','test_mathfunc2.TestMathFunc2']))
with open('report.html','wb') as f:#输出到HTML
runner=HTMLTestRunner(stream=f,title='mathfunc test report',description='generated by HTMLTestRunner.',verbosity= 2)
runner.run(suite)
'''
#用addTests方法+TestLoader+loadTestsFromTestCase()方法传入TestCase名称即可
suite=unittest.TestSuite ()#创建测试用例
loader=unittest.TestLoader()#加载测试用例
suite.addTests(loader.loadTestsFromTestCase(TestMathFunc))
#生成结果到文件
with open('report.txt','w+') as f:
runner=unittest.TextTestRunner(stream=f,verbosity= 2)
runner.run(suite)
'''
生成的txt和HTML文件内容:
test_add (test.test_mathfunc.TestMathFunc)
Test method add(a,b) ... ok
test_divide (test.test_mathfunc.TestMathFunc)
Test method divide(a,b) ... ok
test_minus (test.test_mathfunc.TestMathFunc)
Test method minus(a,b) ... ok
test_multi (test.test_mathfunc.TestMathFunc)
Test method multi(a,b) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
mathfunc test report
mathfunc test report
Start Time: 2019-05-21 09:30:57
Duration: 0:00:00
Status: Pass 8
generated by HTMLTestRunner.
Test Group/Test case
Count
Pass
Fail
Error
View
test_mathfunc.TestMathFunc: Test mathFunc.py
4
4
0
0
Detail
test_add: Test method add(a,b)
pass
test_divide: Test method divide(a,b)
pass
test_minus: Test method minus(a,b)
pass
test_multi: Test method multi(a,b)
pass
test_mathfunc2.TestMathFunc2
4
4
0
0
Detail
test_add: Test method add(a,b)
pass
test_divide: Test method divide(a,b)
pass
test_minus: Test method minus(a,b)
pass
test_multi: Test method multi(a,b)
pass
Total
8
8
0
0