Python单元测试框架

单元测试概述

Python 测试框架
从软件架构的⻆度来说,测试最重要的步骤是在软件开发的时候界⼊⽐较好,所以在早期测试的界⼊,
从软件经济学的⻆度上来说,发现的问题解决成本低,投⼊的资源⽐较少。因此,对⼀个测试的系统,
开始最佳的测试就是源代码级别的测试,也就是单元测试阶段,这个过程也被成为⽩盒测试。单元测试
是最基本也是最底层的测试类型,单元测试应⽤于最基本的软件代码,如类,函数。⽅法等,单元测试
通过可执⾏的断⾔检查被测单元的输出是否满⾜预期结果。在测试⾦字塔的理论上来说,越往下的测试
投⼊资源越⾼,得到的回报率越⼤,⻅测试⾦字塔模型:

抛开软件架构的层⾯,在⾃动化测试的体系中,单元测试框架以及单元测试的知识体系是必须要掌握的

技能之⼀,单元测试的知识体系是⾃动化测试⼯程师以及测试开发⼯程师的知识体系之⼀,⽽且是必须
具备的知识之⼀。在 Python 语⾔中应⽤最⼴泛的单元测试框架是 unittest pytest,unittest 属于标准库,
只要安装了 Python 解释器后就可以直接导⼊使⽤了 ,pytest 是第三⽅的库,需要单独的安装。单元测试框
架的知识体系就围绕 unittest pytest 来讲解。

Version:0.9 StartHTML:0000000105 EndHTML:0000000476 StartFragment:0000000141 EndFragment:0000000436

unittest 各个组件的介绍

unittest 各个组件的介绍

测试⽤例:
测试类继承 unittest 模块中的 TestCase 类后,依据继承的这个类来设置⼀个新的测试⽤例类和测试⽅
unittest 测试固件详解
unittest 中测试固件依据⽅法可以分为两种执⾏⽅式,⼀种是测试固件只执⾏⼀次,另外⼀种是测试固
件每次都执⾏,下⾯依据具体的案例来讲解⼆者。
1 、测试固件每次均执⾏
#!/usr/bin/env python
# -*-coding:utf-8 -*-
import unittest
from selenium import webdriver
class ApiTest(unittest.TestCase): 
 def setUp(self): 
 self.driver=webdriver.Chrome() 
 self.driver.maximize_window() 
 self.driver.get('http://www.baidu.com') 
 self.driver.implicitly_wait(30) 
 
 def tearDown(self): 
 self.driver.quit() 
 
 def test_baidu_title(self): 
 self.assertEqual(self.driver.title,'百度⼀下,你就知道') 
 
 def test_baidu_url(self):
 self.assertEqual(self.driver.current_url,'https://www.baidu.com/')
 
if __name__ == '__main__': 
 unittest.main(verbosity=2)

执⾏如上的代码后,它的顺序为:
测试固件 --> 测试⽤例,测试固件 --> 测试⽤例
2 、测试固件只执⾏⼀次
使⽤的是类⽅法 , 这样测试固件只会执⾏⼀次的,⻅案例的代码:
#!/usr/bin/env python
# -*-coding:utf-8 -*-
import unittest
from selenium import webdriver
class ApiTest(unittest.TestCase): 
 @classmethod 
 def setUpClass(cls): 
 cls.driver=webdriver.Chrome() 
 cls.driver.maximize_window() 
 cls.driver.get('http://www.baidu.com') 
测试⽤例执⾏顺序详解
在unittest中,测试点的执⾏顺序是依据ascill码来执⾏的,也就是说根据ASCII码的顺序加载,数字与字
⺟的顺序为:0-9,A-Z,a-z,所以以A开头的测试⽤例⽅法会优先执⾏,以a开头会后执⾏。也就是根据
数字的⼤⼩从⼩到⼤执⾏的,切记数字的⼤⼩值的是不包含test,值的是test后⾯的测试点的数字⼤⼩,
⻅案例代码:
执⾏的顺序为:test_001,下来时test_002
当然测试点不会单纯是数字的,也有字符串的,在python中,字符串与数字的转换为:
chr():数字转为字⺟
ord():字⺟转为数字
 cls.driver.implicitly_wait(30) 
 
 @classmethod 
 def tearDownClass(cls): 
 cls.driver.quit() 
 
 def test_baidu_title(self): 
 self.assertEqual(self.driver.title,'百度⼀下,你就知道') 
 
 def test_baidu_url(self):
 self.assertEqual(self.driver.current_url,'https://www.baidu.com/')
 
if __name__ == '__main__': 
 unittest.main(verbosity=2)

测试⽤例执⾏顺序详解
unittest 中,测试点的执⾏顺序是依据 ascill 码来执⾏的,也就是说根据 ASCII 码的顺序加载,数字与字
⺟的顺序为: 0-9 A-Z a-z ,所以以 A 开头的测试⽤例⽅法会优先执⾏,以 a 开头会后执⾏。也就是根据
数字的⼤⼩从⼩到⼤执⾏的,切记数字的⼤⼩值的是不包含 test ,值的是 test 后⾯的测试点的数字⼤⼩,
⻅案例代码:
#!/usr/bin/python3
#coding:utf-8
import unittest
class Api(unittest.TestCase):
 def test_001(self):
 pass
 def test_002(self):
 pass
if __name__ == '__main__':
 unittest.main(verbosity=2)

当然测试点不会单纯是数字的,也有字符串的,在 python 中,字符串与数字的转换为:
chr(): 数字转为字⺟
ord(): 字⺟转为数字
⻅测试点的名称是字符串的执⾏顺序:
#!/usr/bin/python3
#coding:utf-8
import unittest
class Api(unittest.TestCase):
 def test_abc(self):
 pass
 def test_acb(self):
 pass
if __name__ == '__main__':
 unittest.main(verbosity=2)

执⾏的顺序为: test_abc, 下来是 test_acb
再看字符串与数字的⽐较:

#coding:utf-8
import unittest
class Api(unittest.TestCase):
 def test_api_0a0(self):
 pass
 def test_api_00a(self):
 pass
if __name__ == '__main__':
 unittest.main(verbosity=2)

verbosity 的详解
unittest , 执⾏的时候默认是 1, 也就是 unittest.main(verbosity=1), verbosity 有三个参数,分别是
0 1 2 ,代表的意思具体如下:
0 (静默模式):仅仅获取总的测试⽤例数以及总的结果
1 (默认模式)
2( 详细模式 ): 测试结果会显示每个测试⽤例的所有相关信息,下⾯分别演示⼏个在错误情况下的显示信
息:
#!/usr/bin/env python
# -*-coding:utf-8 -*-
import unittest
from selenium import webdriver
class Baidu(unittest.TestCase): 
 def setUp(self): 
 self.driver=webdriver.Chrome() 
 self.driver.implicitly_wait(30) 
 self.driver.maximize_window() 
 self.driver.get('http://www.baidu.com') 
 
 def test_baidu_title(self): 
 self.assertEqual(self.driver.title,'百度⼀下你就知道') 
 
 def tearDown(self): 
 self.driver.quit()
 
if __name__ == '__main__': 
 unittest.main()

⻅默认情况的错误信息显示:
Ran 1 test in 59.437s
FAILED (failures=1)
百度⼀下你就知道 != 百度⼀下,你就知道
Expected : 百度⼀下,你就知道 Actual : 百度⼀下你就知道
Traceback (most recent call last): File "D:\Program Files\JetBrains\PyCharm
2018.3.5\helpers\pycharm\teamcity\diff_tools.py", line 32, in patched_equals old(self, first, second,
msg) File "C:\Python37\lib\unittest\case.py", line 839, in assertEqual assertion_func(first, second,
msg=msg) File "C:\Python37\lib\unittest\case.py", line 1220, in assertMultiLineEqual
self.fail(self. formatMessage(msg, standardMsg)) File "C:\Python37\lib\unittest\case.py", line 680, in
fail raise self.failureException(msg) AssertionError: ' 百度⼀下,你就知道 ' != ' 百度⼀下你就知道 '
百度⼀下,你就知道 ? -
百度⼀下你就知道
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "C:\Python37\lib\unittest\case.py", line 59, in
testPartExecutor yield File "C:\Python37\lib\unittest\case.py", line 615, in run testMethod() File
"D:\git\GITHUB\pyUnit\unit01.py", line 16, in test_baidu_title self.assertEqual(self.driver.title,' 百度
⼀下你就知道 ')
编写测试⽤例注意事项
测试⽤例注意事项如下:
1 、在⼀个测试类⾥⾯,每⼀个测试⽅法都是以 test 开头的, test 不能是中间或者尾部,必须是开头,建
test_
2 、每⼀个测试⽤例⽅法都应该有注释信息,这样在测试报告就会显示具体的测试点的检查点
3 、在⾃动化测试中,每个测试⽤例都必须得有断⾔,⽆断⾔的⾃动化测试⽤例是⽆效的
4 、最好⼀个测试⽤例⽅法对应⼀个业务测试点,不要多个业务检查点写⼀个测试⽤例
5 、如果涉及到业务逻辑的处理,最好把业务逻辑的处理⽅法放在断⾔前⾯,这样做的⽬的是不要因为业
务逻辑执⾏错误导致断⾔也是失败
6 、测试⽤例名称最好规范,有约束
7 、是否先写⾃动化测试的测试代码,在使⽤⾃动化测试⽅式写,本⼈觉得没必要,毕竟能够做⾃动化测
试的都具备了功能测试的基本⽔平,所以没必要把⼀个业务的检查点写多次,浪费时间和⼈⼒成本。⻅
案例代码
#!/usr/bin/env python
# -*-coding:utf-8 -*-
import requests
import unittest
import os
class ApiTest(unittest.TestCase): 
 def writeBookID(self,bookID): 
 with open(os.path.join(os.path.dirname(__file__),'bookID'),'w') as f:
 f.write(str(bookID))
 
 @property 
 def readBookID(self): 
 with open(os.path.join(os.path.dirname(__file__), 'bookID'), 'r') as f:
 return f.read()
 
 def creeteBook(self): 
命令⾏执⾏信息
当然编写的测试点也是可以依据命令⾏来进⾏执⾏的,unit03.py的源码如下:
执⾏的命令⾏命令为:
python3 -m unittest unit03.py —>按模块执⾏
python3 -m unittest unit03.UiTest —>按测试类来执⾏
python3 -m unittest unit03.UiTest.test_001 —>按每个测试⽤例来执⾏
python3 -m unittest -v unit03 —>显示详细的信息
 dict1={'author':'⽆涯','name':'Python⾃动化测试实战','done':True}
 r=requests.post(url='http://localhost:5000/v1/api/books',json=dict1)
 self.writeBookID(r.json()[0]['datas']['id'])
 return r 
 
 def test_api_book(self): 
 '''API测试:查询书籍''' 
 self.creeteBook()
 r=requests.get(url='http://localhost:5000/v1/api/book/{0}'.
 format(self.readBookID))
 self.delBook() 
 self.assertEqual(r.json()['datas'][0]['name'],'Python⾃动化测试实战')
 self.assertEqual(r.json()['datas'][0]['id'],int(self.readBookID))
 
 def delBook(self): 
 r=requests.delete(url='http://localhost:5000/v1/api/book/{0}'.
 format(self.readBookID))
 
if __name__ == '__main__': 
 unittest.main(verbosity=2)
命令⾏执⾏信息
当然编写的测试点也是可以依据命令⾏来进⾏执⾏的, unit03.py 的源码如下:
#!/usr/bin/python3
#coding:utf-8
import unittest
class UiTest(unittest.TestCase):
 def test_001(self):
 pass
 def test_002(self):
 pass
if __name__ == '__main__':
 unittest.main(verbosity=2)
执⾏的命令⾏命令为:
python3 -m unittest unit03.py —> 按模块执⾏
python3 -m unittest unit03.UiTest —> 按测试类来执⾏
python3 -m unittest unit03.UiTest.test_001 —> 按每个测试⽤例来执⾏
python3 -m unittest -v unit03 —> 显示详细的信息
python3 -m unittest discover -p 'unit03.py' —> 测试发现
测试套件详解
按测试⽤例执⾏
只添加需要执⾏的测试⽤例
suite=unittest.TestSuite()suite.addTest(Api('test_001'))
unittest.TextTestRunner(verbosity=2).run(suite)
按测试类执⾏
import unittest
class Api(unittest.TestCase): 
 def test_001(self): 
 pass 
 
 def test_002(self): 
 pass
 
if __name__ == '__main__': 
 suite=unittest.TestLoader().loadTestsFromTestCase(Api)
 unittest.TextTestRunner(verbosity=2).run(suite)
⻅测试类的另外⼀种执⾏的⽅式,也就是 makeSuite() 类,⻅源码:
import unittest
class ApiTestCase(unittest.TestCase): 
 def test_001(self): 
 pass 
 
 def test_002(self): 
 pass
 
class AppTestCase(unittest.TestCase): 
 def test_001(self): 
 pass 
 
 def test_002(self): 
 pass
 
if __name__ == '__main__': 
 apiSuite=unittest.makeSuite(ApiTestCase,'test')
 appSuite=unittest.makeSuite(AppTestCase,'test')
 suite=unittest.TestSuite(apiSuite,appSuite)
按测试模块来执⾏
⻅⾃定义测试套件的源码:
 unittest.TextTestRunner(verbosity=2).run(suite)
按测试模块来执⾏
import unittest
class ApiTestCase(unittest.TestCase): 
 def test_001(self): 
 pass 
 
 def test_002(self): 
 pass
 
class AppTestCase(unittest.TestCase): 
 def test_003(self): 
 pass
 
 def test_004(self): 
 pass
if __name__ == '__main__': 
 suite=unittest.TestLoader().loadTestsFromModule('unit02.py')
 unittest.TextTestRunner(verbosity=2).run(suite)
⾃定义测试套件
⻅⾃定义测试套件的源码:
#!/usr/bin/python3
#coding:utf-8
import unittest
class UiTest(unittest.TestCase):
 def test_001(self):
 pass
 def test_002(self):
 pass
 @staticmethod
 def suite():
 suite=unittest.TestSuite()
 suite.addTest(UiTest('test_001'))
 return suite
if __name__ == '__main__':
 unittest.TextTestRunner().run(UiTest.suite())

优化测试套件
把测试套件分离成⼀个⽅法,然后直接调⽤这个⽅法就可以了,⻅实现的代码:
import unittest
from selenium import webdriver
class Baidu(unittest.TestCase): 
 def setUp(self): 
 self.driver=webdriver.Chrome() 
 self.driver.implicitly_wait(30) 
 self.driver.maximize_window() 
 self.driver.get('http://www.baidu.com') 
 
 def test_baidu_title(self): 
 '''验证:验证百度⾸⻚的title是否正确''' 
 self.assertEqual(self.driver.title,'百度⼀下,你就知道') 
 
 def test_baidu_url(self): 
 '''验证:验证百度⾸⻚的url是否正确'''
 self.assertEqual(self.driver.current_url,'https://www.baidu.com/') 
 
 def tearDown(self): 
 self.driver.quit() 
 
 @staticmethod 
 def suite(): 
 tests=unittest.TestLoader().loadTestsFromTestCase(Baidu) 
 return tests
 
if __name__ == '__main__': 
 unittest.TextTestRunner(verbosity=2).run(Baidu.suite())
分离测试套件
利⽤继承的思想分离出测试套件 , 测试类只需要继承分离的类就可以了,⻅ init.py 的源码:
import unittest
from selenium import webdriver
class Init(unittest.TestCase): 
 def setUp(self): 
 self.driver=webdriver.Chrome() 
 self.driver.implicitly_wait(30) 
 self.driver.maximize_window() 
 self.driver.get('http://www.baidu.com') 
 
 def tearDown(self): 
 self.driver.quit()
⻅测试类的源码 , 只需要继承 Init 类就可以了,⻅源码:
from init import Init
class Baidu(Init): 
 def test_baidu_title(self):
 '''验证:验证百度⾸⻚的title是否正确''' 
 self.assertEqual(self.driver.title,'百度⼀下,你就知道') 
 
 def test_baidu_url(self):
 '''验证:验证百度⾸⻚的url是否正确'''
 self.assertEqual(self.driver.current_url,'https://www.baidu.com/')
 
if __name__ == '__main__': 
 unittest.main(verbosity=2)
unittest 的参数化
parameterized 实战
unittest 的测试框架中,可以结合 ddt 的模块来达到参数化的应⽤,当然关于 ddt 库的应⽤在数据驱动
⽅⾯有很详细的解释,这⾥就直接说另外的⼀个第三⽅的库 parameterized, 安装的命令为:
pip3 install parameterized
安装成功后,这⾥就以⼀个两个数相加的案例来演示它的应⽤,实现的源码如下:
#!/usr/bin/python3
#coding:utf-8
import unittest
from parameterized import parameterized,param
def add(a,b):
 return a+b
class AddTest(unittest.TestCase):
 def setUp(self) -> None:
 pass
 def tearDown(self) -> None:
 pass
 @parameterized.expand([
 param(1,1,2),
 param(2,2,4),
 param(1.0,1.0,2.0),
 param('hi',' wuya','hi wuya')
 ])
 def test_add(self,a,b,result):
 self.assertEqual(add(a,b),result)
if __name__ == '__main__':
 unittest.main(verbosity=2)
执⾏后它的输出信息为:
test_add_0 (__main__.AddTest) ... ok
test_add_1 (__main__.AddTest) ... ok
test_add_2 (__main__.AddTest) ... ok
test_add_3_hi (__main__.AddTest) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
UI ⾃动化测试中, parameterized 也是特别的有⽤,如针对⼀个登录案例的测试,针对登录就会有很
多的测试案例的,主要是⽤户名和密码的 input 表单的验证以及错误信息的验证,下⾯就结合具体的案例
来看它在 UI ⾃动化测试中的应⽤,案例源码:
#!/usr/bin/python3
#coding:utf-8
#author:⽆涯
#time:2019-11-23 14:00:00
import unittest
from selenium import webdriver
from parameterized import parameterized,param
import time as t
class UiTest(unittest.TestCase):
 def setUp(self) -> None:
 self.driver=webdriver.Chrome()
 self.driver.maximize_window()
 self.driver.get('https://mail.sina.com.cn/')
 self.driver.implicitly_wait(30)
 def tearDown(self) -> None:
 self.driver.quit()
 @parameterized.expand([
 param('','','请输⼊邮箱名'),
 param('[email protected]','asdfgh','登录名或密码错误'),
 param('esrtyu','dsgftyj','您输⼊的邮箱名格式不正确'),
 param('dsf','','您输⼊的邮箱名格式不正确')
 ])
 def test_sina_login(self,username,password,result):
 '''sina邮箱⾸⻚的登录验证'''
 self.driver.find_element_by_id('freename').send_keys(username)
 self.driver.find_element_by_id('freepassword').send_keys(password)
 self.driver.find_element_by_link_text('登录').click()
 t.sleep(3)
 divText=self.driver.find_element_by_xpath('
 /html/body/div[1]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]').te
xt
 self.assertTrue(divText,result)
if __name__ == '__main__':
 unittest.main(verbosity=2)
执⾏如上的代码后,输出的结果信息为:
collecting ... collected 4 items
f2.py::UiTest::test_sina_login_0_
f2.py::UiTest::test_sina_login_1_sadfdgrh_sina_com
f2.py::UiTest::test_sina_login_2_esrtyu
f2.py::UiTest::test_sina_login_3_dsf
==================== 4 passed in 23.39 seconds =====================

其实 parameterized 在执⾏的时候,它的原理和核⼼思想是在执⾏的过程中,它把列表⾥⾯的值⾸先进
⾏循环,然后把 param 元组⾥⾯的值赋值给测试⽅法⾥⾯对应的参数 username,password,result ,如第
⼀个元组,它的实际赋值也就是为 :username='',password='',result=' 请输⼊邮箱名 ', 具体可以打断点
(切记 debug 的模式执⾏),看具体赋值的过程,⻅如下显示的截图信息 (thinkpad 的电脑操作截图信
) ddt 实战
ddt 是第三⽅的库,需要单独的安装,安装的命令为:
pip3 install ddt
ddt 它能够实现在⼀个同样的测试步骤中,使⽤⼀个测试点的测试代码,实现多个测试场景的验证,这⾥
就以 sina 的邮箱登录为案例,来演示它在 UI ⾃动化测试中的应⽤,具体代码如下:
#!/usr/bin/python3
#coding:utf-8
import unittest
from selenium import webdriver
import time as t
import ddt
@ddt.ddt
class SinaTest(unittest.TestCase):
 def setUp(self) -> None:
 self.driver=webdriver.Chrome()
 self.driver.maximize_window()
 self.driver.get('https://mail.sina.com.cn/')
 self.driver.implicitly_wait(30)
 def tearDown(self) -> None:
 self.driver.quit()
 @ddt.data(
 ('','','请输⼊邮箱名'),
 ('admin','admin','您输⼊的邮箱名格式不正确'),
 ('[email protected]','asdfghjk','登录名或密码错误'))
 @ddt.unpack
 def test_sina_login(self,username,password,result):
 self.driver.find_element_by_id('freename').send_keys(username)
 self.driver.find_element_by_id('freepassword').send_keys(password)
 self.driver.find_element_by_link_text('登录').click()
 t.sleep(3)
 
divText=self.driver.find_element_by_xpath('/html/body/div[1]/div/div[2]/div/di
v/div[4]/div[1]/div[1]/div[1]/span[1]').text
 self.assertEqual(divText,result)
if __name__ == '__main__':
 unittest.main(verbosity=2)
断⾔的详解
unit test 的单元测试框架中,涉及到的断⾔主要如下,⾸先来看源码⾥⾯的断⾔⽅法:
def assertListEqual(self, list1, list2, msg=None):
 """A list-specific equality assertion.
 Args:
 list1: The first list to compare.
 list2: The second list to compare.
 msg: Optional message to use on failure instead of a list of
 differences.
 """
 self.assertSequenceEqual(list1, list2, msg, seq_type=list)
def assertTupleEqual(self, tuple1, tuple2, msg=None):
 """A tuple-specific equality assertion.
 Args:
 tuple1: The first tuple to compare.
 tuple2: The second tuple to compare.
 msg: Optional message to use on failure instead of a list of
 differences.
 """
 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
def assertSetEqual(self, set1, set2, msg=None):
 """A set-specific equality assertion.
 Args:
 set1: The first set to compare.
 set2: The second set to compare.
 msg: Optional message to use on failure instead of a list of
 differences.
 assertSetEqual uses ducktyping to support different types of sets, and
 is optimized for sets specifically (parameters must support a
 difference method).
 """
 try:
 difference2 = set2.difference(set1)
 except TypeError as e:
 self.fail('invalid type when attempting set difference: %s' % e)
 except AttributeError as e:
 self.fail('second argument does not support set difference: %s' % e)
 if not (difference1 or difference2):
 return
 lines = []
 if difference1:
 lines.append('Items in the first set but not the second:')
 for item in difference1:
 lines.append(repr(item))
 if difference2:
 lines.append('Items in the second set but not the first:')
 for item in difference2:
 lines.append(repr(item))
 standardMsg = '\n'.join(lines)
 self.fail(self._formatMessage(msg, standardMsg))
def assertIn(self, member, container, msg=None):
 """Just like self.assertTrue(a in b), but with a nicer default message."""
 if member not in container:
 standardMsg = '%s not found in %s' % (safe_repr(member),
 safe_repr(container))
 self.fail(self._formatMessage(msg, standardMsg))
def assertNotIn(self, member, container, msg=None):
 """Just like self.assertTrue(a not in b), but with a nicer default
message."""
 if member in container:
 standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
 safe_repr(container))
 self.fail(self._formatMessage(msg, standardMsg))
def assertIs(self, expr1, expr2, msg=None):
 """Just like self.assertTrue(a is b), but with a nicer default message."""
 if expr1 is not expr2:
 standardMsg = '%s is not %s' % (safe_repr(expr1),
 safe_repr(expr2))
 self.fail(self._formatMessage(msg, standardMsg))
def assertIsNot(self, expr1, expr2, msg=None):
 """Just like self.assertTrue(a is not b), but with a nicer default
message."""
 if expr1 is expr2:
 standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
 self.fail(self._formatMessage(msg, standardMsg))
def assertDictEqual(self, d1, d2, msg=None):
 self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
 self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
 if d1 != d2:
 standardMsg = '%s != %s' % _common_shorten_repr(d1, d2)
 diff = ('\n' + '\n'.join(difflib.ndiff(
 pprint.pformat(d1).splitlines(),
 pprint.pformat(d2).splitlines())))
 standardMsg = self._truncateMessage(standardMsg, diff)
 self.fail(self._formatMessage(msg, standardMsg))
def assertDictContainsSubset(self, subset, dictionary, msg=None):
 """Checks whether dictionary is a superset of subset."""
 warnings.warn('assertDictContainsSubset is deprecated',
 DeprecationWarning)
 missing = []
 mismatched = []
 for key, value in subset.items():
 if key not in dictionary:
 missing.append(key)
 elif value != dictionary[key]:
 mismatched.append('%s, expected: %s, actual: %s' %
 (safe_repr(key), safe_repr(value),
 safe_repr(dictionary[key])))
 if not (missing or mismatched):
 return
 standardMsg = ''
 if missing:
 standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
 missing)
 if mismatched:
 if standardMsg:
 standardMsg += '; '
 standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
 self.fail(self._formatMessage(msg, standardMsg))
def assertCountEqual(self, first, second, msg=None):
 """An unordered sequence comparison asserting that the same elements,
 regardless of order. If the same element occurs more than once,
 it verifies that the elements occur the same number of times.
 self.assertEqual(Counter(list(first)),
 Counter(list(second)))
 Example:
 - [0, 1, 1] and [1, 0, 1] compare equal.
 - [0, 0, 1] and [0, 1] compare unequal.
 """
 first_seq, second_seq = list(first), list(second)
 try:
 first = collections.Counter(first_seq)
 second = collections.Counter(second_seq)
 except TypeError:
 # Handle case with unhashable elements
 differences = _count_diff_all_purpose(first_seq, second_seq)
 else:
 if first == second:
 return
 differences = _count_diff_hashable(first_seq, second_seq)
 if differences:
 standardMsg = 'Element counts were not equal:\n'
 lines = ['First has %d, Second has %d: %r' % diff for diff in
differences]
 diffMsg = '\n'.join(lines)
 standardMsg = self._truncateMessage(standardMsg, diffMsg)
 msg = self._formatMessage(msg, standardMsg)
 self.fail(msg)
def assertMultiLineEqual(self, first, second, msg=None):
 """Assert that two multi-line strings are equal."""
 self.assertIsInstance(first, str, 'First argument is not a string')
 self.assertIsInstance(second, str, 'Second argument is not a string')
 if first != second:
 # don't use difflib if the strings are too long
 if (len(first) > self._diffThreshold or
 len(second) > self._diffThreshold):
 self._baseAssertEqual(first, second, msg)
 firstlines = first.splitlines(keepends=True)
 secondlines = second.splitlines(keepends=True)
 if len(firstlines) == 1 and first.strip('\r\n') == first:
 firstlines = [first + '\n']
 secondlines = [second + '\n']
 standardMsg = '%s != %s' % _common_shorten_repr(first, second)
 diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines))
 standardMsg = self._truncateMessage(standardMsg, diff)
 self.fail(self._formatMessage(msg, standardMsg))
def assertLess(self, a, b, msg=None):
 """Just like self.assertTrue(a < b), but with a nicer default message."""
 if not a < b:
 standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b))
 self.fail(self._formatMessage(msg, standardMsg))
def assertLessEqual(self, a, b, msg=None):
 """Just like self.assertTrue(a <= b), but with a nicer default message."""
 if not a <= b:
 standardMsg = '%s not less than or equal to %s' % (safe_repr(a),
safe_repr(b))
 self.fail(self._formatMessage(msg, standardMsg))
def assertGreater(self, a, b, msg=None):
 """Just like self.assertTrue(a > b), but with a nicer default message."""
 if not a > b:
 standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b))
 self.fail(self._formatMessage(msg, standardMsg))
def assertGreaterEqual(self, a, b, msg=None):
 """Just like self.assertTrue(a >= b), but with a nicer default message."""
 if not a >= b:
 standardMsg = '%s not greater than or equal to %s' % (safe_repr(a),
safe_repr(b))
 self.fail(self._formatMessage(msg, standardMsg))
def assertIsNone(self, obj, msg=None):
 """Same as self.assertTrue(obj is None), with a nicer default message."""
 if obj is not None:
 standardMsg = '%s is not None' % (safe_repr(obj),)
 self.fail(self._formatMessage(msg, standardMsg))
def assertIsNotNone(self, obj, msg=None):
 """Included for symmetry with assertIsNone."""
 if obj is None:
 standardMsg = 'unexpectedly None'
 self.fail(self._formatMessage(msg, standardMsg))
def assertIsInstance(self, obj, cls, msg=None):
 """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
 default message."""
 if not isinstance(obj, cls):
 standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls)
 self.fail(self._formatMessage(msg, standardMsg))
def assertNotIsInstance(self, obj, cls, msg=None):
 """Included for symmetry with assertIsInstance."""
 if isinstance(obj, cls):
 standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
 self.fail(self._formatMessage(msg, standardMsg))
def assertRaisesRegex(self, expected_exception, expected_regex,
 *args, **kwargs):
 """Asserts that the message in a raised exception matches a regex.
 Args:
 expected_exception: Exception class expected to be raised.
 expected_regex: Regex (re.Pattern object or string) expected
 to be found in error message.
 args: Function to be called and extra positional args.
 kwargs: Extra kwargs.
 msg: Optional message used in case of failure. Can only be used
 when assertRaisesRegex is used as a context manager.
 """
 context = _AssertRaisesContext(expected_exception, self, expected_regex)
 return context.handle('assertRaisesRegex', args, kwargs)
def assertWarnsRegex(self, expected_warning, expected_regex,
 *args, **kwargs):
 """Asserts that the message in a triggered warning matches a regexp.
 Basic functioning is similar to assertWarns() with the addition
 that only warnings whose messages also match the regular expression
 are considered successful matches.
 Args:
 expected_warning: Warning class expected to be triggered.
 expected_regex: Regex (re.Pattern object or string) expected
 to be found in error message.
 args: Function to be called and extra positional args.
 kwargs: Extra kwargs.
 msg: Optional message used in case of failure. Can only be used
 when assertWarnsRegex is used as a context manager.
 """
 context = _AssertWarnsContext(expected_warning, self, expected_regex)
 return context.handle('assertWarnsRegex', args, kwargs)
def assertRegex(self, text, expected_regex, msg=None):
 """Fail the test unless the text matches the regular expression."""
 if isinstance(expected_regex, (str, bytes)):
 assert expected_regex, "expected_regex must not be empty."
 expected_regex = re.compile(expected_regex)
 if not expected_regex.search(text):
 standardMsg = "Regex didn't match: %r not found in %r" % (
 expected_regex.pattern, text)
 # _formatMessage ensures the longMessage option is respected
 msg = self._formatMessage(msg, standardMsg)
 raise self.failureException(msg)
def assertNotRegex(self, text, unexpected_regex, msg=None):
 """Fail the test if the text matches the regular expression."""
 if isinstance(unexpected_regex, (str, bytes)):
 unexpected_regex = re.compile(unexpected_regex)
 match = unexpected_regex.search(text)
 if match:
 standardMsg = 'Regex matched: %r matches %r in %r' % (
 text[match.start() : match.end()],
 unexpected_regex.pattern,
 text)
 # _formatMessage ensures the longMessage option is respected
 msg = self._formatMessage(msg, standardMsg)
 raise self.failureException(msg)
def _deprecate(original_func):
 def deprecated_func(*args, **kwargs):
 warnings.warn(
 'Please use {0} instead.'.format(original_func.__name__),
 DeprecationWarning, 2)
 return original_func(*args, **kwargs)
 return deprecated_func
# see #9424
failUnlessEqual = assertEquals = _deprecate(assertEqual)
failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
failUnless = assert_ = _deprecate(assertTrue)
failUnlessRaises = _deprecate(assertRaises)
failIf = _deprecate(assertFalse)
assertRaisesRegexp = _deprecate(assertRaisesRegex)
assertRegexpMatches = _deprecate(assertRegex)
assertNotRegexpMatches = _deprecate(assertNotRegex)
具体详解如下:

 

assertEqual
assertEqual() 是验证两个⼈相等,值的是数据类型与内容也是相等的,⻅案例代码:
#!/usr/bin/python3
#coding:utf-8
import unittest
from selenium import webdriver
class BaiduUI(unittest.TestCase):
 def setUp(self):
 self.driver=webdriver.Chrome()
 self.driver.maximize_window()
 self.driver.implicitly_wait(30)
 self.driver.get('http://www.baidu.com/')
 def tearDown(self):
 self.driver.quit()
 def test_baidu_title(self):
 self.assertEqual(self.driver.title,'百度⼀下,你就知道')
if __name__ == '__main__':
unittest.main(verbosity=2)
再看内容⼀致,但是数据类型不⼀致的案例代码:        
#!/usr/bin/python3
#coding:utf-8
import unittest
from selenium import webdriver
class BaiduUI(unittest.TestCase):
 def setUp(self):
 self.driver=webdriver.Chrome()
 self.driver.maximize_window()
 self.driver.implicitly_wait(30)
 self.driver.get('http://www.baidu.com/')
 def tearDown(self):
 self.driver.quit()
 def test_baidu_title(self):
 self.assertEqual(self.driver.title,'百度⼀下,你就知道'.encode('utf-8'))
if __name__ == '__main__':
 unittest.main(verbosity=2)
执⾏如上的代码,会显示失败
assertTrue
返回的是 bool 类型,也就是对被测试的对象进⾏验证,如果返回的是 boolean 类型并且是 true ,那么结
果验证通过,那么⽅法 assertFlase() 验证的是被测试对象返回的内容是 false, ⻅案例代码:
import unittest
from selenium import webdriver
def login(username,password):
 if username=='wuya' and password=='admin':
 return True
 else:
 return False
class LoginTest(unittest.TestCase):
 def test_login_true(self):
 self.assertTrue(login('wuya','admin'))
 def test_login_notTrue(self):
 self.assertTrue(login('wuya','weike'))
 def test_login_false(self):
 self.assertFalse(login('wuya','weike'))
if __name__ == '__main__':
 unittest.main(verbosity=2)
assertIn
assertIn() 值的是⼀个值是否包含在另外⼀个值⾥⾯,在这⾥特别的强调⼀下,在 assertIn() 的⽅法⾥⾯,
有两个参数,那么值的包含其实就是第⼆个实际参数包含第⼀个实际参数。与之相反的⽅法是
assergNotIn(), ⻅案例代码:
#!/usr/bin/python3
#coding:utf-8
import unittest
from selenium import webdriver
def login(username,password):
 if username=='wuya' and password=='admin':
 return '⽆涯课堂'
 else:
 return '请登录'
class LoginTest(unittest.TestCase):
 def test_login_in(self):
 self.assertIn(login('wuya','admin'),'⽆涯课堂')
 def test_login_in_second(self):
 self.assertIn(login('wuya','admin'),'⽆涯课堂专注于测试技术的提升和培训')
 def test_login_not_in(self):
 self.assertNotIn(login('wuya','weike'),'⽆涯课堂')
if __name__ == '__main__':
 unittest.main(verbosity=2)
下⾯我结合 UI ⾃动化测试来具体演示它的实际应⽤,如测试百度⾸⻚的 url ,那么依据该断⾔的⽅法,可
以编写⽇下的⼏个测试⽅法(测试点只有⼀个,主要是演示测试断⾔),案例源码如下:
#!/usr/bin/python3
#coding:utf-8
#author:⽆涯
#time:2019-11-23 14:00:00
import unittest
from selenium import webdriver
class UiTest(unittest.TestCase):
 def setUp(self) -> None:
 self.driver=webdriver.Chrome()
 self.driver.maximize_window()
 self.driver.get('http://www.baidu.com')
 self.driver.implicitly_wait(30)
 def tearDown(self) -> None:
 self.driver.quit()
 def test_baidu_title_001(self):
 self.assertIn('百度',self.driver.title)
 def test_baidu_title_002(self):
 self.assertIn('百度⼀下,你就知道',self.driver.title)
if __name__ == '__main__':
 unittest.main(verbosity=2)
UI ⾃动化测试中,断⾔相对来说很好处理,在 API 的⾃动化测试中,很多时候返回很多的响应数据,但
是⼀般需要断⾔好⼏个的字段,这个时候 assertIn() ⽅法就可以使⽤了,如接⼝返回的响应数据为:

 "datas": [
 {
 "author": "⽆涯",
 "done": true,
 "id": 1,
 "name": "Python⾃动化测试实战"
 }
 ],
 "msg": "ok",
 "status": 0
}
#!/usr/bin/python3
#coding:utf-8
#author:⽆涯
#time:2019-11-23 14:00:00
from flask import jsonify,Flask
from flask_restful import Api,Resource
app=Flask(__name__)
api=Api(app=app)
books=[
 {
 'id':1,
 'author':'⽆涯',
 'name':'Python⾃动化测试实战',
 "done":True
 }
]
@app.route('/v1/books',methods=['GET'])
def get_books():
 return jsonify({'status':0,'msg':'ok','datas':books})
if __name__ == '__main__':
 app.run(debug=True)
#!/usr/bin/python3
#coding:utf-8
#author:⽆涯
#time:2019-11-23 14:00:00
import unittest
import requests
class Api(unittest.TestCase):
 def test_api_books(self):
 '''获取所有书的信息'''
 r=requests.get(url='http://localhost:5000/v1/books')
 self.assertIn('Python⾃动化测试',r.json()['datas'][0]['name'])
if __name__ == '__main__':
 unittest.main(verbosity=2)

猜你喜欢

转载自blog.csdn.net/weixin_43886221/article/details/129884051