Page Object是Selenium自动化测试项目开发实践的最佳设计模式之一,通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化,只需要调整页面元素封装的代码,提高测试用例的可维护性。
本项目以163邮箱登录为例,163登录脚本(非PO模式)请查看博客中的随笔信息。
1、项目目录结构及相关介绍

test_programe 测试项目
- driver:用于存放驱动
- mail:用于存放163登录项目的测试用例、测试报告以及测试数据等
- run_all_test.py:用于运行项目自动化用例
mail目录
- data:存放测试数据
- report:存放HTML测试不报告,其中该目录下的image目录用于存放测试过程中的截图
- test_case:用于存放测试用例
test_case目录
- model:存放配置函数及公共类
- page_object:用于存放页面对象
- login_case.py:测试对象用例
2、编写公共模块:
编写驱动文件... est_programemail est_casemodeldriver.py
1 from selenium.webdriver import Remote
2 from selenium import webdriver
3 #启动浏览器驱动
4 def browser():
5 driver = webdriver.Chrome()
6 '''
7 #可以启动到远程主机中,运行自动化测试
8 host = '127.0.0.1:4444' #运行主机:端口号(本机默认:127.0.0.1:4444)
9 dc = {'browserName': 'chrome'} #指定浏览器
10 driver = Remote(command_execute='http://' + host + '/wd/hub',
11 desired_capabilities=dc)
12 '''
13 return driver
14 '''
15 #用于测试该脚本是否有效
16 if __name__ == '__main__':
17 dr = browser()
18 '''
编写测试公共类... est_programemail est_casemodelmyunit.py
from time import sleep
from .function import insert_img
import unittest
from .driver import browser
class MyTest(unittest.TestCase):
def setUp(self):
self.driver = browser()
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def tearDown(self):
self.driver.quit()
定义截图函数... est_programemail est_casemodelfunction.py
1 from selenium import webdriver
2 import os
3 #截图函数
4 def insert_img(driver, file_name):
5 base_dir = os.path.dirname(os.path.dirname(__file__))
6 #print(base_dir)
7 base_dir = str(base_dir)
8 #print(base_dir)
9 base_dir = base_dir.replace('\','/')
10 #print(base_dir)
11 base = base_dir.split('/mail')[0]
12 #print(base)
13 file_path = base + '/mail/report/image/' + file_name
14 driver.get_screenshot_as_file(file_path)
15
16 '''
17 #用于验证该脚本是否有效
18 if __name__ == '__main__':
19 driver = webdriver.Chrome()
20 driver.get('http://www.baidu.com')
21 insert_img(driver, 'baidu.jpg')
22 driver.quit()
23 '''
3、编写Page Object
创建基础类... est_programemail est_casepage_objectase.py
1 from selenium import webdriver 2 from selenium.webdriver.common.by import By 3 from time import sleep 4 import unittest 5 #基本层 6 class Base(object): 7 8 9 def __init__(self, driver, base_url = 'http://mail.163.com'): 10 self.driver = driver 11 self.base_url = base_url 12 self.timeout = 30 13 14 def _open(self, url): 15 url_ = self.base_url + url 16 #print(url_) 17 self.driver.maximize_window() 18 self.driver.get(url_) 19 sleep(2) 20 assert self.driver.current_url == url_, 'Did ont land on %s' % url_ 21 22 def open(self): 23 self._open(self.url) 24 25 #*参数个数不是固定的(By.ID, 'kw') 26 def find_element(self, *loc): 27 return self.driver.find_element(*loc) 28 29 def iframe(self, iframeid): 30 return self.driver.switch_to.frame(iframeid) 31 32 def iframe_out(self): 33 return self.driver.switch_to.default_content() 34 35
创建163邮箱登录对象类... est_programemail est_casepage_objectlogin_page.py
1 from selenium.webdriver.common.action_chains import ActionChains 2 from selenium.webdriver.common.by import By 3 from time import sleep 4 from .base import Base 5 6 #页面对象(PO)登录页面 7 class LoginPage(Base): 8 url = '/' 9 login_iframe_loc = 'x-URS-iframe' 10 login_username_text_loc = (By.NAME, 'email') 11 login_password_text_loc = (By.NAME, 'password') 12 login_button_loc = (By.ID, 'dologin') 13 login_erro_hint_loc = (By.CLASS_NAME, 'ferrorhead') 14 15 #把每一个元素封装成一个方法 16 def login_iframe(self): 17 self.iframe(self.login_iframe_loc) 18 19 def login_iframe_out(self): 20 self.iframe_out() 21 22 def login_username(self, text): 23 self.find_element(*self.login_username_text_loc).send_keys(text) 24 25 def login_password(self, text): 26 self.find_element(*self.login_password_text_loc).send_keys(text) 27 28 def login_button(self): 29 self.find_element(*self.login_button_loc).click() 30 31 def login_error_hint(self): 32 self.login_iframe() 33 return self.find_element(*self.login_erro_hint_loc).text 34 self.login_iframe_out() 35 36 def login_action(self, username, password): 37 self.login_iframe() 38 self.login_username(username) 39 self.login_password(password) 40 self.login_button() 41 self.login_iframe_out()
创建163邮箱登录成功后对象类... est_programemail est_casepage_objectmail_page.py
1 from selenium.webdriver.common.action_chains import ActionChains 2 from selenium.webdriver.common.by import By 3 from time import sleep 4 from .base import Base 5 6 class MailPage(Base): 7 url = '/' 8 login_success_user_loc = (By.ID, 'spnUid') 9 10 def login_success_user(self): 11 return self.find_element(*self.login_success_user_loc).text
4、编写测试用例
创建mail登录类... est_programemail est_caselogin_case.py
1 from time import sleep
2 import unittest, random, sys
3 from model import myunit, function
4 from page_object.login_page import LoginPage
5 from page_object.mail_page import MailPage
6 sys.path.append('./model')
7 sys.path.append('./page_obj')
8
9 class LoginTest(myunit.MyTest):
10
11 def test_login_user_pwd_null(self):
12 '''用户名、密码为空登录'''
13 po = LoginPage(self.driver)
14 po.open()
15 po.login_action('','')
16 sleep(2)
17 self.assertEqual(po.login_error_hint(),'请输入帐号')
18 function.insert_img(self.driver, 'user_pwd_null.jpg')
19
20 def test_login_pwd_null(self):
21 '''密码为空登录'''
22 po = LoginPage(self.driver)
23 po.open()
24 po.login_action('abc','')
25 sleep(2)
26 self.assertEqual(po.login_error_hint(),'请输入密码')
27 function.insert_img(self.driver, 'pwd_null.jpg')
28
29 def test_login_user_pwd_error(self):
30 '''用户名或密码错误'''
31 po = LoginPage(self.driver)
32 po.open()
33 character = random.choice('zyxwvutsrqponmlkjihgfedcba')
34 username = "test" + character
35 po.login_action(username,"$#%#")
36 sleep(2)
37 #print(po.login_error_hint())
38 self.assertEqual(po.login_error_hint(),'帐号或密码错误')
39 function.insert_img(self.driver, "user_pwd_error.jpg")
40
41 def test_login_success(self):
42 '''用户名、密码正确,登录成功'''
43 po = LoginPage(self.driver)
44 po.open()
45 user = "ldq791918813"
46 po.login_action(user,"xiuxiu060801zhu")
47 sleep(2)
48 po2 = MailPage(self.driver)
49 #print(po2.login_success_user())
50 self.assertEqual(po2.login_success_user(),user+"@163.com")
51 function.insert_img(self.driver, "success.jpg")
5、执行测试用例
创建用例执行代码... est_programe
1 import unittest, time
2 from HTMLTestRunner import HTMLTestRunner
3 from email.mime.text import MIMEText
4 from email.header import Header
5 import smtplib, os
6
7 #发送测试报告,需要配置你的邮箱账号
8 def send_mail(file_new):
9 f = open(file_new, 'rb')
10 mail_body = f.read()
11 f.close()
12 msg = MIMEText(mail_body, 'html', 'utf-8')
13 msg['Subject'] = Header("自动化测试报告", 'utf-8')
14 msg['From']= 'ldq791918813@163.com'
15 msg['To']= '791918813@qq.com'
16 smtp = smtplib.SMTP()
17 smtp.connect("smtp.163.com")
18 smtp.login("ldq791918813@163.com", "密码")
19 smtp.sendmail("ldq791918813@163.com","791918813@qq.com",msg.as_string())
20 smtp.quit()
21 print('email has send out!')
22
23 #查找测试报告目录,找到最新生成的测试报告文件
24 def new_report(testreport):
25 lists = os.listdir(testreport)
26 lists.sort(key=lambda fn: os.path.getmtime(testreport + '\' + fn))
27 file_new = os.path.join(testreport, lists[-1])
28 print(file_new)
29 return file_new
30
31 #指定测试用例为当前文件夹下的test_case目录
32 test_dir = './mail/test_case'
33 test_report = 'D:\sublimePython\test_programe\mail\report'
34 discover = unittest.defaultTestLoader.discover(test_dir, pattern = '*_case.py')
35
36 if __name__ == "__main__":
37
38 now = time.strftime("%Y-%m-%d %H_%M_%S")
39 filename = test_report + '/' + now + 'result.html'
40 fp = open(filename, 'wb')
41 #runner = unittest.TextTestRunner()
42 runner = HTMLTestRunner(stream=fp,
43 title='测试报告',
44 description="运行环境:windows 7, Chrome")
45 runner.run(discover)
46 fp.close()
47
48 new_report = new_report(test_report)
49 send_mail(new_report)
6、HTML测试报告:

7、截图信息:

