UI自动化测试-页面对象设计(PO)
一、为什么使用页面对象设计
在之前的UI自动化测试的学习中,我们认为我们的有些代码模块,还是可以进一步有优化的,因而页面对象设计应运而生。
使用页面对象设计的优势在于:
- 可以创建跨多个测试用例共享的代码
- 减少一些重复代码的书写
- 如果用户界面发生了一些变化,我们只需要维护一个地方,这样不仅对于修改以及维护的成本会降低,而且也会提高我们测试人员的工作效率。
二、目录结构设计
之所以建成这样,是因为:
- base的包下主要编写基础的代码,可以理解为基础层
- page包下主要存放对象层的代码,可以理解为对象层
- test报下主要存放编写的测试模块的代码,可以理解为测试层
- utils下存放的主要是工具类的代码,比如针对JSON文件,YAML文件的处理
- common下主要存放的是公共类的代码,比如文件目录的处理
- data主要存放测试过程中使用到的数据
- report主要存储的是测试报告这部分
三、页面对象设计模式(PO:Page Object)
3-1:基础层(base)
首先在基础层里新建一个名为:“base.py”的文件,内容如下:
1 from selenium import webdriver 2 from selenium.webdriver.support.expected_conditions import NoSuchElementException 3 from selenium.webdriver.common.by import By 4 import time as t 5 6 7 8 class WebDriver(object): 9 def __init__(self,driver): 10 # self.driver=webdriver.Chrome() 11 # self.driver是driver实例化后的对象 12 self.driver=driver 13 14 15 def findElement(self,*loc): 16 '''单个元素定位的方式''' 17 try: 18 # loc即表示括号里面是动态参数,*也表示括号中的元素是元组类型 19 return self.driver.find_element(*loc) 20 except NoSuchElementException as e: 21 return e.args[0] 22 23 24 def findElements(self,*loc): 25 '''多个元素定位的方式''' 26 try: 27 return self.driver.find_elements(*loc) 28 except NoSuchElementException as e: 29 return e.args[0]
3-2:对象层(page)
其次在对象层里新建两个名为:“login_sina.py”和“register_sina.py”的文件,我们以新浪邮箱的“登陆”和“注册”为例,内容如下:
“login_sina.py”代码如下:
1 from selenium import webdriver 2 from selenium.webdriver.support.expected_conditions import NoSuchElementException 3 from selenium.webdriver.common.by import By 4 import time as t 5 from base.base import WebDriver 6 7 class Login(WebDriver): 8 username=(By.ID,'freename') 9 password=(By.ID,'freepassword') 10 loginButton=(By.LINK_TEXT,'登录') 11 usernameDev=(By.CSS_SELECTOR,'body > div.mailLoginBox > div > div.mainBox.bg1 > div > div > div:nth-child(6) > div.loginBox > div.freeMailbox > div.freeError > span.loginError.tip11') 12 passwordDev=(By.CSS_SELECTOR,'body > div.mailLoginBox > div > div.mainBox.bg1 > div > div > div:nth-child(6) > div.loginBox > div.freeMailbox > div.freeError > span.loginError.tip13') 13 emailsuccess=(By.ID,'right_nav_1') 14 autologin=(By.ID,'store1') 15 16 17 def inputusername(self,username): 18 '''输入框:输入邮箱名/手机号''' 19 t.sleep(3) 20 self.findElement(*self.username).send_keys(username) 21 22 def inputpassword(self,password): 23 '''输入框:输入密码''' 24 t.sleep(3) 25 self.findElement(*self.password).send_keys(password) 26 27 def clicklogin(self): 28 '''点击登录''' 29 t.sleep(3) 30 self.findElement(*self.loginButton).click() 31 32 def getusernameDev(self): 33 '''获取错误的用户名''' 34 t.sleep(3) 35 return self.findElement(*self.usernameDev).text 36 37 def getpasswordDev(self): 38 '''获取错误的密码''' 39 t.sleep(3) 40 return self.findElement(*self.passwordDev).text 41 42 def getemailsucces(self): 43 '''获取登陆成功的[首页]文字''' 44 t.sleep(3) 45 return self.findElement(*self.emailsuccess).text 46 47 def autologinyes(self): 48 '''自动勾选[自动登录]''' 49 t.sleep(3) 50 return self.findElement(*self.autologin).is_selected() 51 52 def autologinno(self): 53 '''取消自动勾选[自动登录]''' 54 t.sleep(3) 55 return self.findElement(*self.autologin).click()
“register_sina.py”代码如下:
1 from selenium.webdriver.common.by import By 2 import time as t 3 from base.base import WebDriver 4 5 6 class Register(WebDriver): 7 register=(By.LINK_TEXT,'注册') 8 agree=(By.ID,'agreement1') 9 lijiregister=(By.CSS_SELECTOR,'body > div.main > div > div > div > div > form:nth-child(2) > div:nth-child(8) > ul > li > div > div > a') 10 emailnull=(By.CSS_SELECTOR,'body > div.main > div > div > div > div > form:nth-child(2) > div:nth-child(7) > ul > li.fiSel.zIndexNew > div.tipText > p > abbr') 11 passwordnull=(By.CSS_SELECTOR,'body > div.main > div > div > div > div > form:nth-child(2) > div:nth-child(7) > ul > li:nth-child(2) > div.tipText > p > abbr') 12 13 14 15 def click_register(self): 16 '''点击新浪邮箱的注册''' 17 t.sleep(3) 18 self.findElement(*self.register).click() 19 20 21 22 def click_agree(self): 23 '''点击勾选协议''' 24 t.sleep(3) 25 self.findElement(*self.agree).click 26 27 28 29 def click_lijiregister(self): 30 '''点击立即注册''' 31 t.sleep(3) 32 self.findElement(*self.lijiregister).click() 33 34 35 36 37 def clickReg(self): #封装 38 self.click_agree() 39 self.click_lijiregister() 40 41 42 43 def getEmail(self): 44 '''获取邮箱一栏的提示信息''' 45 t.sleep(3) 46 return self.findElement(*self.emailnull).text 47 48 49 50 def getpassword(self): 51 '''获取密码一栏的提示信息''' 52 t.sleep(3) 53 return self.findElement(*self.passwordnull).text
3-3: 测试层(test)
再者,在测试层,也就是test包下创建两个“test_sina_login.py”和“test_sina_register.py”
的文件,内容为:
“test_sina_login.py”文件的内容如下:
1 from selenium import webdriver 2 from page.login_sina import Login 3 import unittest 4 from utils.operationJson import readJson 5 6 class TestLogin(unittest.TestCase,Login): 7 def setUp(self) -> None: 8 self.driver=webdriver.Chrome() 9 self.driver.maximize_window() 10 self.driver.get("https://mail.sina.com.cn/#") 11 self.driver.implicitly_wait(30) 12 13 def tearDown(self) -> None: 14 self.driver.quit() 15 16 def test_username_null(self): 17 '''登录验证:账户为空错误提示信息验证''' 18 self.login(username='',password='12334') 19 # self.assertEqual(self.getuserDev(),"请输入邮箱名") 20 self.assertEqual(self.getusernameDev(), readJson()["nullEmail"]) 21 22 def test_email_format_error(self): 23 '''登录验证:验证邮箱格式错误''' 24 self.login(username='gfs',password='hhhhf') 25 # self.assertEqual(self.getuserDev(),"您输入的邮箱名格式不正确") 26 self.assertEqual(self.getusernameDev(),readJson()["formatEmail"]) 27 28 def test_username_password_error(self): 29 '''登录验证:验证登录账户错误''' 30 self.login(username='gfhhgh@sina.com',password='xgfj') 31 # self.assertEqual(self.getuserDev(),"登录名或密码错误") 32 self.assertEqual(self.getusernameDev(),readJson()["loginError"]) 33 34 def test_password_null(self): 35 '''登录验证:验证密码为空错误提示信息''' 36 self.login(username='gfhhgh@sina.com',password='') 37 # self.assertEqual(self.getpssswdDev(),"请输入密码") 38 self.assertEqual(self.getpasswordDev(), readJson()["nullPasswd"]) 39 40 41 def test_login_success(self): 42 '''登录验证:验证登录成功的账户''' 43 self.login(username='wuya1303@sina.com',password='admin123') 44 self.assertEqual(self.getemailsucces(),"wuya1303@sina.com") 45 46 def test_auto_login(self): 47 '''自动登陆勾选''' 48 self.assertTrue(self.autologinyes()) 49 50 51 def test_auto_login_cancel(self): 52 '''取消自动登陆勾选''' 53 self.autologinno() 54 self.assertFalse(self.autologinyes()) 55 56 if __name__ == '__main__': 57 unittest.main()
“test_sina_register.py”文件的内容如下:
1 from selenium import webdriver 2 from page.register_sina import Register 3 import unittest 4 from utils.operationJson import readJson 5 import time as t 6 7 class TestLogin(unittest.TestCase,Register): 8 def setUp(self) -> None: 9 self.driver=webdriver.Chrome() 10 self.driver.maximize_window() 11 self.driver.get("https://mail.sina.com.cn/#") 12 self.driver.implicitly_wait(30) 13 14 def tearDown(self) -> None: 15 self.driver.quit() 16 17 def test_email_null(self): 18 '''注册之后:邮箱名为空错误提示信息验证''' 19 nowpage=self.driver.current_window_handle 20 self.click_register() 21 allpages=self.driver.window_handles 22 t.sleep(3) 23 for hander in allpages: 24 if nowpage!=hander: 25 self.driver.switch_to.window(hander) 26 self.clickReg() 27 self.assertEqual(self.getEmailDev(),'请输入邮箱名') 28 29 if __name__ == '__main__': 30 unittest.main()
3-4:公共类(common)
然后,在common包下创建一个“public.py”的文件,里面主要编写针对文件路径的处理,具体内容如下:
1 import os 2 3 def base_dir(): 4 return os.path.dirname(os.path.dirname(__file__)) 5 6 def filePath(directory,filename): 7 ''' 8 获取文件的详细路径 9 :param directory: 10 :param filename: 11 :return: 12 ''' 13 return os.path.join(base_dir(),directory,filename)
3-5:数据驱动(data)
下来在data的文件夹下创建一个“login.json”的文件,把使用到的数据分离到login.json的文件里面,内容为:
{ "nullEmail": "请输入邮箱名", "formatEmail": "您输入的邮箱名格式不正确", "loginError": "登录名或密码错误", "nullPasswd": "请输入密码" }
这样,我们就可以将“test_sina_login.py”文件测试代码改为这样:
这样,我们就可以将“test_sina_register.py”文件测试代码改为这样:
3-6:工具类(utils)
在具体的工具类中编写针对JSON文件的处理,创建的模块名称为:operationJson.py,具体源码为:
1 import json 2 from common.public import filePath 3 4 def readJson(): 5 return json.load(open(filePath( 6 directory="data", 7 filename="login.json"),encoding="utf-8")) 8 print(readJson())
3-7:测试固件分离
在page包下新建一个“init.py”文件,来具体分离我们的测试固件,内容为:
1 from selenium import webdriver 2 import unittest 3 4 class Init(unittest.TestCase): 5 def setUp(self) -> None: 6 self.driver=webdriver.Chrome() 7 self.driver.maximize_window() 8 self.driver.implicitly_wait(30) 9 self.driver.get("https://mail.sina.com.cn/#") 10 11 def tearDown(self) -> None: 12 self.driver.quit()
3-8:完善后的测试层
“test_sina_login.py”文件测试代码完善为这样:
1 from selenium import webdriver 2 from page.login_sina import Login 3 import unittest 4 from utils.operationJson import readJson 5 from page.init import Init 6 7 class TestLogin(Init,Login): 8 9 10 def test_username_null(self): 11 '''登录验证:账户为空错误提示信息验证''' 12 self.login(username='',password='12334') 13 # self.assertEqual(self.getuserDev(),"请输入邮箱名") 14 self.assertEqual(self.getusernameDev(),readJson()["nullEmail"]) 15 16 def test_email_format_error(self): 17 '''登录验证:验证邮箱格式错误''' 18 self.login(username='gfs',password='hhhhf') 19 # self.assertEqual(self.getuserDev(),"您输入的邮箱名格式不正确") 20 self.assertEqual(self.getusernameDev(),readJson()["formatEmail"]) 21 22 def test_username_password_error(self): 23 '''登录验证:验证登录账户错误''' 24 self.login(username='gfhhgh@sina.com',password='xgfj') 25 # self.assertEqual(self.getuserDev(),"登录名或密码错误") 26 self.assertEqual(self.getusernameDev(),readJson()["loginError"]) 27 28 def test_password_null(self): 29 '''登录验证:验证密码为空错误提示信息''' 30 self.login(username='gfhhgh@sina.com',password='') 31 # self.assertEqual(self.getpssswdDev(),"请输入密码") 32 self.assertEqual(self.getpasswordDev(),readJson()["nullPasswd"]) 33 34 35 def test_login_success(self): 36 '''登录验证:验证登录成功的账户''' 37 self.login(username='wuya1303@sina.com',password='admin123') 38 self.assertEqual(self.getemailsucces(),"首页") 39 40 def test_auto_login(self): 41 '''自动登陆勾选''' 42 self.assertTrue(self.autologinyes()) 43 44 45 def test_auto_login_cancel(self): 46 '''取消自动登陆勾选''' 47 self.autologinno() 48 self.assertFalse(self.autologinyes()) 49 50 if __name__ == '__main__': 51 unittest.main()
“test_sina_register.py”文件测试代码完善为这样:
1 from selenium import webdriver 2 from page.register_sina import Register 3 import unittest 4 from utils.operationJson import readJson 5 import time as t 6 from page.init import Init 7 8 class TestLogin(Init,Register): 9 10 11 def test_email_null(self): 12 '''注册之后:邮箱名为空错误提示信息验证''' 13 nowpage=self.driver.current_window_handle 14 self.click_register() 15 allpages=self.driver.window_handles 16 t.sleep(3) 17 for hander in allpages: 18 if nowpage!=hander: 19 self.driver.switch_to.window(hander) 20 self.clickReg() 21 # self.assertEqual(self.getEmailDev(),'请输入邮箱名') 22 self.assertEqual(self.getEmailDev(),readJson()["nullEmail"])
3-9:报告(report)
在运行测试层的代码之前,先在PageObject工程下新建一个”main.py"文件,在该文件里写如下内容:
1 import unittest 2 import HTMLTestRunner #在上一节里,已经在电脑里下载了这个第三方库 3 import time 4 from common.public import * 5 6 ''' 7 import os 8 因为在“common”包下“public"里存放的路径为: 9 print(os.path.dirname(os.path.dirname(__file__))) 运行结果:D:\test\code\PageObject 10 所以:要拼接路径 11 ''' 12 def allTests(): 13 '''获取要执行的模块''' 14 suite=unittest.TestLoader().discover( 15 start_dir=os.path.join(base_dir(), 'test'), 16 pattern='test_*.py') 17 return suite 18 19 def getNowTime(): 20 return time.strftime("%y-%m-%d %H_%M_%S",time.localtime(time.time())) 21 22 23 def run(): 24 filename=filePath(directory='report',filename=getNowTime()+'.html') 25 fp=open(file=filename,mode='wb') 26 runner=HTMLTestRunner.HTMLTestRunner( 27 stream=fp, 28 title='新浪邮箱自动化测试', 29 description='自动化测试内容' 30 ) 31 runner.run(allTests()) 32 33 if __name__ == '__main__': 34 run()
然后运行测试文件,会在report文件夹下,生成具体的测试报告,用浏览器打开便可看到: