单元框架测试
UnitTest组件
unittest是属于Python语言的单元测试框架,它的核心组件具体可以总结为如下:
testCase:编写的每个具体的测试场景
测试固件(勾子方法):初始化和清理
testSuite:测试套件是testCase的集合
testRunner:具体执行被编写的测试套件或者是测试用例
testReport:测试报告反馈实际的测试结果
自动化测试用例要素包含:
1、前提条件 setUp
2、测试步骤
3、结果验证
4、清理 tearDown
测试固件
测试固件(勾子方法):初始化和清理
测试固件表示一个测试用例或者多个测试以及清理工作所需要的设置或者准备,
首先导入相关库
from selenium import webdriver import unittest
测试套件
testRunner:具体执行被编写的测试套件或者是测试用例
测试套件顾名思义就是相关测试用例的集合。在unittest中主要通过TestSuite类提供对测试套件的支持
代码实现为:
class BaiduTest(unittest.TestCase): def setUp(self) -> None: self.driver=webdriver.Chrome() self.driver.get('https://www.baidu.com/') self.driver.implicitly_wait(15) def earDown(self): self.driver.quit() def test_baidu_title(self): '''百度测试:验证百度首页的title''' # assert self.driver.title=='百度一下,你就知道' self.assertEqual(self.driver.title,'百度一下,你就知道') def test_baidu_url(self): '''百度测试:验证百度首页的url''' assert self.driver.current_url=='https://www.baidu.com/'#预期结果 def test_baidu_live(self): '''百度测试:验证点击视频后跳转到直播''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('直播').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://live.baidu.com/') def test_baidu_map(self): '''百度测试:验证点击视频后跳转到地图''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('地图').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertTrue(self.driver.current_url.startswith('https://map.baidu.com/')) def test_baidu_vido(self): '''百度测试:验证点击视频后跳转到视频''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('视频').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://haokan.baidu.com/?sfrom=baidu-top') if __name__ == '__main__': unittest.main(verbosity=2)
运行不难发现,运行过程中打开个关闭浏览器这部分每执行一个测试用例都得执行一次:
class BaiduTest(unittest.TestCase): def setUp(self) -> None: self.driver=webdriver.Chrome() self.driver.get('https://www.baidu.com/') self.driver.implicitly_wait(15) def earDown(self): self.driver.quit()
这时;我们想要他只执行一次
#测试固件只执行一次 class BaiduTest(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.driver=webdriver.Chrome() cls.driver.get('https://www.baidu.com/') cls.driver.implicitly_wait(15) @classmethod def tearDownClass(cls) -> None: cls.driver.quit() def test_baidu_title(self): '''百度测试:验证百度首页的title''' # assert self.driver.title=='百度一下,你就知道' self.assertEqual(self.driver.title,'百度一下,你就知道') def test_baidu_url(self): '''百度测试:验证百度首页的url''' assert self.driver.current_url=='https://www.baidu.com/'#预期结果 def test_baidu_live(self): '''百度测试:验证点击视频后跳转到直播''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('直播').click() #找到直播并点击
#窗口的切换 allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://live.baidu.com/') self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_map(self): '''百度测试:验证点击视频后跳转到地图''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('地图').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertTrue(self.driver.current_url.startswith('https://map.baidu.com/')) self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_vido(self): '''百度测试:验证点击视频后跳转到视频''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('视频').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://haokan.baidu.com/?sfrom=baidu-top') self.driver.close() self.driver.switch_to.window(newHandle) if __name__ == '__main__': unittest.main(verbosity=2)
测试套件详解
UnitTest的测试框架中提供了很丰富的测试套件,所谓测试套件其实我们可以把它理解为测试用例的集合,
或者可以说理解为一个容器,在这个容器里面可以存放很多的测试用例。下面详细的说明下各个不同测试套件
的应用和案例实战。
按测试类执行
按测试类执行,可以理解为在测试套件中,我们按测试类的方式来进行执行,那么也就不需要在乎一个
测试类里面有多少测试用例,我们是以测试类为单位来进行执行,测试类里面有多少的测试用例,我们都会
进行执行,下面详细的演示这部分的具体应用,具体代码如下:
from selenium import webdriver import unittest class BaiduTest(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.driver=webdriver.Chrome() cls.driver.get('https://www.baidu.com/') cls.driver.implicitly_wait(15) @classmethod def tearDownClass(cls) -> None: cls.driver.quit() def test_baidu_title(self): '''百度测试:验证百度首页的title''' # assert self.driver.title=='百度一下,你就知道' self.assertEqual(self.driver.title,'百度一下,你就知道') def test_baidu_url(self): '''百度测试:验证百度首页的url''' assert self.driver.current_url=='https://www.baidu.com/'#预期结果 def test_baidu_live(self): '''百度测试:验证点击视频后跳转到直播''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('直播').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://live.baidu.com/') self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_map(self): '''百度测试:验证点击视频后跳转到地图''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('地图').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertTrue(self.driver.current_url.startswith('https://map.baidu.com/')) self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_vido(self): '''百度测试:验证点击视频后跳转到视频''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('视频').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://haokan.baidu.com/?sfrom=baidu-top') self.driver.close() self.driver.switch_to.window(newHandle) if __name__ == '__main__': suit=unittest.TestLoader().loadTestsFromTestCase(BaiduTest) unittest.TextTestRunner().run(suit)
这样就能按类为单位来执行了
按测试模块执行
思维按测试模块来执行,就是以模块为单位来进行执行,那么其实在一个模块里面可以编写很多的类,
下面通过详细的代码演示下这部分,具体代码如下
class BaiduTest(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.driver=webdriver.Chrome() cls.driver.get('https://www.baidu.com/') cls.driver.implicitly_wait(15) @classmethod def tearDownClass(cls) -> None: cls.driver.quit() def test_baidu_title(self): '''百度测试:验证百度首页的title''' # assert self.driver.title=='百度一下,你就知道' self.assertEqual(self.driver.title,'百度一下,你就知道') def test_baidu_url(self): '''百度测试:验证百度首页的url''' assert self.driver.current_url=='https://www.baidu.com/'#预期结果 def test_baidu_live(self): '''百度测试:验证点击视频后跳转到直播''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('直播').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://live.baidu.com/') self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_map(self): '''百度测试:验证点击视频后跳转到地图''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('地图').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertTrue(self.driver.current_url.startswith('https://map.baidu.com/')) self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_vido(self): '''百度测试:验证点击视频后跳转到视频''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('视频').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://haokan.baidu.com/?sfrom=baidu-top') self.driver.close() self.driver.switch_to.window(newHandle) if __name__ == '__main__': suit=unittest.TestLoader().loadTestsFromTestCase('test_baidu_按模块执行.py') #执行"test_baidu_按模块执行.py"模块
unittest.TextTestRunner().run(suit)
这样就能按模块为单位来执行了
多模块执行
每个模块都有不同的测试用例但有都是一个类型的,因此我们想要把他们一起执行,具体代码为:
def getTests(): '''加载所有模块''' suite=unittest.TestLoader().discover( #找到被执行模块的路径 start_dir=os.path.dirname(__file__), #加载路径下所有的test_开头的测试模块 pattern='test_*.py' ) return suite def runSuite(): unittest.TextTestRunner().run(getTests()) runSuite()
参数化
在unittest测试框架中,参数化使用的库为:parameterized 安装方式为:
pip3 install parameterized
参数化:
我们把相同的测试步骤,不同的测试场景,那么我们就可以使用参数化了
可以解决的问题是可以使用少量的测试代码,来覆盖更多的测试场景
以sina为例,我们对它的登录页面进行检测,具体代码实现为
class Testsina(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(15) def tearDown(self): self.driver.quit() def test_sina_longin_Ture(self): '''验证邮箱是否勾选''' islongin=self.driver.find_element_by_id('store1') self.assertTrue(islongin.is_selected()) def test_sina_longin_False(self): '''验证邮箱未勾选''' islongin=self.driver.find_element_by_id('store1') islongin.click() self.assertFalse(islongin.is_selected()) def test_sina_longin(self): '''验证邮箱密码为空的信息''' self.driver.find_element_by_class_name('loginBtn').click() divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]') self.assertEqual(divText.text,'请输入邮箱名') def test_sina_longin(self): '''验证邮箱格式不正确的信息''' self.driver.find_element_by_class_name('placeholder').send_keys('qweretghmnbv xcsf5') self.driver.find_element_by_class_name('loginBtn').click() divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]') self.assertEqual(divText.text,'您输入的邮箱名格式不正确') def test_sina_longin(self): '''验证邮箱密码不匹配的信息''' self.driver.find_element_by_id('freename').send_keys('12827362@sina.cn') self.driver.find_element_by_id('freepassword').send_keys('1234455543564r') self.driver.find_element_by_class_name('loginBtn').click() t.sleep(3) divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]') self.assertEqual(divText.text,'登录名或密码错误') if __name__ == '__main__': unittest.main(verbosity=2)
从代码中我们能发现每次验证我们都需要输入登录名和密码,只是它每次的值都不同,name我们就可以对它这部分进行一个参数化,以此来减少繁琐的工作过程,具体代码为;
class Testsina(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(15) def tearDown(self): self.driver.quit() @parameterized.expand([ param('','','请输入邮箱名'), param('werds','qwer','您输入的邮箱名格式不正确'), param('wfxyw@sina.cn','duikgbcf','登录名或密码错误') ]) def test_sina_longin(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_class_name('loginBtn').click() t.sleep(3) divText=self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div/div[4]/div[1]/div[1]/div[1]/span[1]') self.assertEqual(divText.text,result) if __name__ == '__main__': unittest.main(verbosity=2)
分离与继承
当我们在测试一个页面的不同模块时,不难发现有一部分的代码我们要重复的写,例如我们在测试百度页面时,
class BaiduTest(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.driver=webdriver.Chrome() cls.driver.get('https://www.baidu.com/') cls.driver.implicitly_wait(15) @classmethod def tearDownClass(cls) -> None: cls.driver.quit()
前提条件和清理这部分一直在重复,因此我们可以把他分离出来,后面的不同模块我们就可以通过继承来完成它。
同时把测试固件分离出来,后面直接继承这样更改域名时只需要更改一个地方。
from selenium import webdriver import unittest class Init(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.driver=webdriver.Chrome() cls.driver.get('https://www.baidu.com/') cls.driver.implicitly_wait(15) @classmethod def tearDownClass(cls) -> None: cls.driver.quit()
class BaiduTest(Init):#继承Init def test_baidu_title(self): '''百度测试:验证百度首页的title''' # assert self.driver.title=='百度一下,你就知道' self.assertEqual(self.driver.title,'百度一下,你就知道') def test_baidu_url(self): '''百度测试:验证百度首页的url''' assert self.driver.current_url=='https://www.baidu.com/'#预期结果 def test_baidu_live(self): '''百度测试:验证点击视频后跳转到直播''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('直播').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://live.baidu.com/') self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_map(self): '''百度测试:验证点击视频后跳转到地图''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('地图').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertTrue(self.driver.current_url.startswith('https://map.baidu.com/')) self.driver.close() self.driver.switch_to.window(newHandle) def test_baidu_vido(self): '''百度测试:验证点击视频后跳转到视频''' newHandle=self.driver.current_window_handle self.driver.find_element_by_link_text('视频').click() allHandlers=self.driver.window_handles for Handler in allHandlers: if Handler!=newHandle: self.driver.switch_to.window(Handler) self.assertEqual(self.driver.current_url,'https://haokan.baidu.com/?sfrom=baidu-top') self.driver.close() self.driver.switch_to.window(newHandle) if __name__ == '__main__': suit=unittest.TestLoader().loadTestsFromTestCase('test_baidu_按模块执行.py') unittest.TextTestRunner().run(suit)
测试报告
测试结束我们需要给出测试报告,一目了然的看出测试结果,代码实现为:
import time import unittest import os import HTMLTestRunner def getTests(): '''加载所有模块''' suite=unittest.TestLoader().discover( #找到被执行模块的路径 start_dir=os.path.dirname(__file__), #加载路径下所有的test_开头的测试模块 pattern='test_*.py' ) return suite def base_dir(): return os.path.dirname(os.path.dirname(__file__)) #获取当前路径的上一级路径 def run(): fp=open(os.path.join(base_dir(),'report','report.html'),'wb') #把测试报告写进规定的文件夹里 runner=HTMLTestRunner.HTMLTestRunner( stream=fp,#流式 title='UI自动化测试报告', description='' ) runner.run(getTests()) if __name__ == '__main__': run()
这样我们就能得出如下所示的报告了。
有多少个通过了,有多少个失败,失败的是什么地方这样一目了然,也方便排查错误。
但是不足的是,第二次生成的报告会覆盖前一次的,所以我们加上时间,具体代码实现:
import time import unittest import os import HTMLTestRunner def getTests(): '''加载所有模块''' suite=unittest.TestLoader().discover( #找到被执行模块的路径 start_dir=os.path.dirname(__file__), #加载路径下所有的test_开头的测试模块 pattern='test_*.py' ) return suite def base_dir(): return os.path.dirname(os.path.dirname(__file__)) #获取当前路径的上一级路径 def getNowtime(): return time.strftime('%y_%m_%d %H_%M_%S',time.localtime(time.time())) 时分秒要大写 def run(): fp=open(os.path.join(base_dir(),'report',getNowtime()+'report.html'),'wb') #把测试报告写进规定的文件夹里 runner=HTMLTestRunner.HTMLTestRunner( stream=fp, title='UI自动化测试报告', description='' ) runner.run(getTests()) if __name__ == '__main__': run()
生成报告:
因为时间不同所以它不会被覆盖