前言
本文整理歸納以往的工作中用到的東西,現(xiàn)匯總成基礎(chǔ)測(cè)試框架提供分享。
框架采用Python/ target=_blank class=infotextkey>Python3 + selenium3 + PO + yaml + ddt + unittest等技術(shù)編寫(xiě)成基礎(chǔ)測(cè)試框架,能適應(yīng)日常測(cè)試工作需要。
1、使用Page Object模式將頁(yè)面定位和業(yè)務(wù)操作分開(kāi),分離測(cè)試對(duì)象(元素對(duì)象)和測(cè)試腳本(用例腳本),一個(gè)頁(yè)面建一個(gè)對(duì)象類(lèi),提高用例的可維護(hù)性;
2、使用yaml管理頁(yè)面控件元素?cái)?shù)據(jù)和測(cè)試用例數(shù)據(jù)。例如元素ID等發(fā)生變化時(shí),不需要去修改測(cè)試代碼,只需要在對(duì)應(yīng)的頁(yè)面元素yaml文件中修改即可;
3、分模塊管理,互不影響,隨時(shí)組裝,即拿即用。
1、測(cè)試框架分層設(shè)計(jì)
- 把常見(jiàn)的操作和查找封裝成基礎(chǔ)類(lèi),不管是什么產(chǎn)品,可直接拿來(lái)復(fù)用
- 業(yè)務(wù)層主要是封裝對(duì)象頁(yè)面類(lèi),一個(gè)頁(yè)面建一個(gè)類(lèi),業(yè)務(wù)層頁(yè)面繼承基礎(chǔ)層
- 用例層針對(duì)產(chǎn)品頁(yè)面功能進(jìn)行構(gòu)造摸擬執(zhí)行測(cè)試
- 框架層提供基礎(chǔ)組件,支撐整個(gè)流程執(zhí)行及功能擴(kuò)展,給用例層提供各頁(yè)面的元素?cái)?shù)據(jù)、用例測(cè)試數(shù)據(jù),測(cè)試報(bào)告輸出等
2、測(cè)試框架目錄結(jié)構(gòu)
如下思維導(dǎo)圖目錄結(jié)構(gòu)介紹:
3、編寫(xiě)用例方法
testinfo:
2 - id: test_login001
3 title: 登錄測(cè)試
4 info: 打開(kāi)抽屜首頁(yè)
5 testcase:
6 - element_info: login-link-a
7 find_type: ID
8 operate_type: click
9 info: 打開(kāi)登錄對(duì)話框
10 - element_info: mobile
11 find_type: ID
12 operate_type: send_keys
13 info: 輸入手機(jī)號(hào)
14 - element_info: mbpwd
15 find_type: ID
16 operate_type: send_keys
17 info: 輸入密碼
18 - element_info: //input[@class='keeplogin']
19 find_type: XPATH
20 operate_type: click
21 info: 單擊取消自動(dòng)登錄單選框
22 - element_info: //span[text()='登錄']
23 find_type: XPATH
24 operate_type: click
25 info: 單擊登錄按鈕
26 - element_info: userProNick
27 find_type: ID
28 operate_type: perform
29 info: 鼠標(biāo)懸停賬戶菜單
30 - element_info: //a[@class='logout']
31 find_type: XPATH
32 operate_type: click
33 info: 選擇退出
34 check:
35 - element_info: //div[@class='box-mobilelogin']/div[1]/span
36 find_type: XPATH
37 info: 檢查輸入手機(jī)號(hào)或密碼,登錄異常提示
38 - element_info: userProNick
39 find_type: ID
40 info: 成功登錄
41 - element_info: reg-link-a
42 find_type: ID
43 info: 檢查退出登錄是否成功
例如,我們要新增登錄功能測(cè)試用例:
首先,只需在testyaml目錄下新增一個(gè)頁(yè)面對(duì)象yaml文件,參考login.yaml格式編寫(xiě)即可。這些文件是提供給封裝頁(yè)面對(duì)象類(lèi)調(diào)用并執(zhí)行定位識(shí)別操作。
1 -
2 id: test_login001.1
3 detail : 手機(jī)號(hào)和密碼為空登錄
4 screenshot : phone_pawd_empty
5 data:
6 phone: ""
7 password: ""
8 check :
9 - 手機(jī)號(hào)不能為空
10 -
11 id: test_login001.2
12 detail : 手機(jī)號(hào)為空登錄
13 screenshot : phone_empty
14 data :
15 phone: ""
16 password : aa
17 check :
18 - 手機(jī)號(hào)不能為空
19 -
20 id: test_login001.3
21 detail : 密碼為空登錄
22 screenshot : pawd_empty
23 data :
24 phone : 13511112222
25 password: ""
26 check :
27 - 密碼不能為空
28 -
29 id: test_login001.4
30 detail : 非法手機(jī)號(hào)登錄
31 screenshot : phone_error
32 data :
33 phone : abc
34 password: aa
35 check :
36 - 手機(jī)號(hào)格式不對(duì)
37 -
38 id: test_login001.5
39 detail : 手機(jī)號(hào)或密碼不匹配
40 screenshot : pawd_error
41 data :
42 phone : 13511112222
43 password: aa
44 check :
45 - 賬號(hào)密碼錯(cuò)誤
46 -
47 id: test_login001.6
48 detail : 手機(jī)號(hào)和密碼正確
49 screenshot : phone_pawd_success
50 data :
51 phone : 13865439800
52 password: ********
53 check :
54 - yingoja
55
56 login_data.yaml
其次,在testdata目錄下新增一個(gè)login_data.yaml文件提供給登錄接口傳參的測(cè)試數(shù)據(jù),編寫(xiě)格式參考login_data.yaml文件。
1 #!/usr/bin/env python
2 # _*_ coding:utf-8 _*_
3 __author__ = 'YinJia'
4
5 import os,sys
6 sys.path.Append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
7 from config import setting
8 from selenium.webdriver.support.select import Select
9 from selenium.webdriver.common.action_chains import ActionChains
10 from selenium.webdriver.common.by import By
11 from public.page_obj.base import Page
12 from time import sleep
13 from public.models.GetYaml import getyaml
14
15 testData = getyaml(setting.TEST_Element_YAML + '/' + 'login.yaml')
16
17 class login(Page):
18 """
19 用戶登錄頁(yè)面
20 """
21 url = '/'
22 dig_login_button_loc = (By.ID, testData.get_elementinfo(0))
23 def dig_login(self):
24 """
25 首頁(yè)登錄
26 :return:
27 """
28 self.find_element(*self.dig_login_button_loc).click()
29 sleep(1)
30
31 # 定位器,通過(guò)元素屬性定位元素對(duì)象
32 # 手機(jī)號(hào)輸入框
33 login_phone_loc = (By.ID,testData.get_elementinfo(1))
34 # 密碼輸入框
35 login_password_loc = (By.ID,testData.get_elementinfo(2))
36 # 取消自動(dòng)登錄
37 keeplogin_button_loc = (By.XPATH,testData.get_elementinfo(3))
38 # 單擊登錄
39 login_user_loc = (By.XPATH,testData.get_elementinfo(4))
40 # 退出登錄
41 login_exit_loc = (By.ID, testData.get_elementinfo(5))
42 # 選擇退出
43 login_exit_button_loc = (By.XPATH,testData.get_elementinfo(6))
44
45 def login_phone(self,phone):
46 """
47 登錄手機(jī)號(hào)
48 :param username:
49 :return:
50 """
51 self.find_element(*self.login_phone_loc).send_keys(phone)
52
53 def login_password(self,password):
54 """
55 登錄密碼
56 :param password:
57 :return:
58 """
59 self.find_element(*self.login_password_loc).send_keys(password)
60
61 def keeplogin(self):
62 """
63 取消單選自動(dòng)登錄
64 :return:
65 """
66 self.find_element(*self.keeplogin_button_loc).click()
67
68 def login_button(self):
69 """
70 登錄按鈕
71 :return:
72 """
73 self.find_element(*self.login_user_loc).click()
74
75 def login_exit(self):
76 """
77 退出系統(tǒng)
78 :return:
79 """
80 above = self.find_element(*self.login_exit_loc)
81 ActionChains(self.driver).move_to_element(above).perform()
82 sleep(2)
83 self.find_element(*self.login_exit_button_loc).click()
84
85 def user_login(self,phone,password):
86 """
87 登錄入口
88 :param username: 用戶名
89 :param password: 密碼
90 :return:
91 """
92 self.open()
93 self.dig_login()
94 self.login_phone(phone)
95 self.login_password(password)
96 sleep(1)
97 self.keeplogin()
98 sleep(1)
99 self.login_button()
100 sleep(1)
101
102 phone_pawd_error_hint_loc = (By.XPATH,testData.get_CheckElementinfo(0))
103 user_login_success_loc = (By.ID,testData.get_CheckElementinfo(1))
104 exit_login_success_loc = (By.ID,testData.get_CheckElementinfo(2))
105
106 # 手機(jī)號(hào)或密碼錯(cuò)誤提示
107 def phone_pawd_error_hint(self):
108 return self.find_element(*self.phone_pawd_error_hint_loc).text
109
110 # 登錄成功用戶名
111 def user_login_success_hint(self):
112 return self.find_element(*self.user_login_success_loc).text
113
114 # 退出登錄
115 def exit_login_success_hint(self):
116 return self.find_element(*self.exit_login_success_loc).text
然后,在page_obj目錄下新增一個(gè)loginPage.py文件,是用來(lái)封裝登錄頁(yè)面對(duì)象類(lèi),執(zhí)行登錄測(cè)試流程操作。
1 #!/usr/bin/env python
2 # _*_ coding:utf-8 _*_
3 __author__ = 'YinJia'
4
5
6 import os,sys
7 sys.path.append(os.path.dirname(os.path.dirname(__file__)))
8 import unittest,ddt,yaml
9 from config import setting
10 from public.models import myunit,screenshot
11 from public.page_obj.loginPage import login
12 from public.models.log import Log
13
14 try:
15 f =open(setting.TEST_DATA_YAML + '/' + 'login_data.yaml',encoding='utf-8')
16 testData = yaml.load(f)
17 except FileNotFoundError as file:
18 log = Log()
19 log.error("文件不存在:{0}".format(file))
20
21 @ddt.ddt
22 class Demo_UI(myunit.MyTest):
23 """抽屜新熱榜登錄測(cè)試"""
24 def user_login_verify(self,phone,password):
25 """
26 用戶登錄
27 :param phone: 手機(jī)號(hào)
28 :param password: 密碼
29 :return:
30 """
31 login(self.driver).user_login(phone,password)
32
33 def exit_login_check(self):
34 """
35 退出登錄
36 :return:
37 """
38 login(self.driver).login_exit()
39
40 @ddt.data(*testData)
41 def test_login(self,datayaml):
42 """
43 登錄測(cè)試
44 :param datayaml: 加載login_data登錄測(cè)試數(shù)據(jù)
45 :return:
46 """
47 log = Log()
48 log.info("當(dāng)前執(zhí)行測(cè)試用例ID-> {0} ; 測(cè)試點(diǎn)-> {1}".format(datayaml['id'],datayaml['detail']))
49 # 調(diào)用登錄方法
50 self.user_login_verify(datayaml['data']['phone'],datayaml['data']['password'])
51 po = login(self.driver)
52 if datayaml['screenshot'] == 'phone_pawd_success':
53 log.info("檢查點(diǎn)-> {0}".format(po.user_login_success_hint()))
54 self.assertEqual(po.user_login_success_hint(), datayaml['check'][0], "成功登錄,返回實(shí)際結(jié)果是->: {0}".format(po.user_login_success_hint()))
55 log.info("成功登錄,返回實(shí)際結(jié)果是->: {0}".format(po.user_login_success_hint()))
56 screenshot.insert_img(self.driver, datayaml['screenshot'] + '.jpg')
57 log.info("-----> 開(kāi)始執(zhí)行退出流程操作")
58 self.exit_login_check()
59 po_exit = login(self.driver)
60 log.info("檢查點(diǎn)-> 找到{0}元素,表示退出成功!".format(po_exit.exit_login_success_hint()))
61 self.assertEqual(po_exit.exit_login_success_hint(), '注冊(cè)',"退出登錄,返回實(shí)際結(jié)果是->: {0}".format(po_exit.exit_login_success_hint()))
62 log.info("退出登錄,返回實(shí)際結(jié)果是->: {0}".format(po_exit.exit_login_success_hint()))
63 else:
64 log.info("檢查點(diǎn)-> {0}".format(po.phone_pawd_error_hint()))
65 self.assertEqual(po.phone_pawd_error_hint(),datayaml['check'][0] , "異常登錄,返回實(shí)際結(jié)果是->: {0}".format(po.phone_pawd_error_hint()))
66 log.info("異常登錄,返回實(shí)際結(jié)果是->: {0}".format(po.phone_pawd_error_hint()))
67 screenshot.insert_img(self.driver,datayaml['screenshot'] + '.jpg')
68
69 if __name__=='__main__':
70 unittest.main()
最后,在testcase目錄下創(chuàng)建測(cè)試用例文件login_sta.py,采用ddt數(shù)據(jù)驅(qū)動(dòng)讀取yaml測(cè)試數(shù)據(jù)文件
綜上所述,編寫(xiě)用例方法只需要按以上四個(gè)步驟創(chuàng)建->編寫(xiě)即可。
執(zhí)行如下主程序,可看輸出的實(shí)際結(jié)果。
1 #!/usr/bin/env python
2 # _*_ coding:utf-8 _*_
3 __author__ = 'YinJia'
4
5 import os,sys
6 sys.path.append(os.path.dirname(__file__))
7 from config import setting
8 import unittest,time
9 from package.htmlTestRunner import HTMLTestRunner
10 from public.models.newReport import new_report
11 from public.models.sendmail import send_mail
12
13 # 測(cè)試報(bào)告存放文件夾,如不存在,則自動(dòng)創(chuàng)建一個(gè)report目錄
14 if not os.path.exists(setting.TEST_REPORT):os.makedirs(setting.TEST_REPORT + '/' + "screenshot")
15
16 def add_case(test_path=setting.TEST_DIR):
17 """加載所有的測(cè)試用例"""
18 discover = unittest.defaultTestLoader.discover(test_path, pattern='*_sta.py')
19 return discover
20
21 def run_case(all_case,result_path=setting.TEST_REPORT):
22 """執(zhí)行所有的測(cè)試用例"""
23 now = time.strftime("%Y-%m-%d %H_%M_%S")
24 filename = result_path + '/' + now + 'result.html'
25 fp = open(filename,'wb')
26 runner = HTMLTestRunner(stream=fp,title='抽屜新熱榜UI自動(dòng)化測(cè)試報(bào)告',
27 description='環(huán)境:windows 7 瀏覽器:chrome',
28 tester='Jason')
29 runner.run(all_case)
30 fp.close()
31 report = new_report(setting.TEST_REPORT) #調(diào)用模塊生成最新的報(bào)告
32 send_mail(report) #調(diào)用發(fā)送郵件模塊
33
34 if __name__ =="__main__":
35 cases = add_case()
36 run_case(cases)
4、測(cè)試結(jié)果展示
- HTML報(bào)告日志
- HTML報(bào)告點(diǎn)擊截圖,彈出截圖
- 測(cè)試報(bào)告通過(guò)的日志
- 自動(dòng)截圖存放指定的目錄
- 郵件測(cè)試報(bào)告
5、總結(jié)
感謝每一個(gè)認(rèn)真閱讀我文章的人!!!