Page Object 设计原理
Page Object设计模式是Selenium自动化测试项目的最佳设计模式之一,强调测试、逻辑、数据和驱动相互分离。
Page Object模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。当页面元素id变化时,只需要更改测试页Class中页面的属性即可。
它的好处如下:
- 集中管理元素对象
- 集中管理一个page内的公共方法
- 后期维护方便
Page Object 的对象
- WebDriver封装
- 这里是对Selenium的封装,完成封装以后的基本封装代码。
- Page 基类
- 设计了一个基本的Page类,以便所有的页面进行继承,该类标明了一个sub page类的基本功能和公共的功能。
- Sub Pages(s)子类
- 具体的页面的类,定义了某个具体的页面的功能。
- Tests 类
- 这部分描述的是具体的测试用例。
- 定义Test Suite
- 多个测试用例添加在一个Test套件里面,一起执行。
- 定义Test Runner
- 设计测试的Runner,开启整个测试,并且对测试的结果生成HTML测试报告,并通过邮件发送到指定邮箱。
- 定义测试的主入口
- 定义测试的主要入口类,代码的入口
HTML测试报告
HTML测试报告需要引入HTMLTestRunner
from ranzhiWeekend import HTMLTestRunner
HTMLTestRunner是基于Python2.7的,我们的课程讲义基于Python3.x,那么需要对这个文件做一定的修改。
测试的示例代码如下
# 声明一个测试套件 suite = unittest.TestSuite() # 添加测试用例到测试套件 suite.addTest(RanzhiTests("test_ranzhi_login")) # 创建一个新的测试结果文件 buf = open("./result.html", "wb") # 声明测试运行的对象 runner = HTMLTestRunner.HTMLTestRunner(stream=buf, title="Ranzhi Test Result", description="Test Case Run Result") # 运行测试,并且将结果生成为HTML runner.run(suite) # 关闭文件输出 buf.close()
集成测试报告
使用邮件发送HTML测试报告的脚本如下
# 打开测试报告结果 f = open("./result.html", "rb") # 将测试结果放到邮件的主体中 mailBody = f.read() # 关闭测试结果的文件 f.close() # 声明一个邮件对象,用刚刚得到的邮件主体 msg = MIMEText(mailBody, "html", "utf-8") # 设置邮件的主题 msg["subject"] = Header("Automation Test Result", "utf-8") # 创建一个SMTP服务对象 # simple message transfer protocol # 简单的消息转移协议 smtpMail = smtplib.SMTP() # 连接SMTP的服务器 smtpMail.connect("mail.51testing.com") # 登录SMTP的服务器 smtpMail.login("liutingli@51testing.com", "123456789") # 使用SMTP的服务器发送邮件 smtpMail.sendmail("liutingli@51testing.com", targetEmail, msg.as_string()) # 退出SMTP对象 smtpMail.quit()
自动化测试框架示例
WebDriver封装
# coding=utf-8 from selenium import webdriver from selenium.webdriver.support.select import Select class AutomateDriver(object): """ a simple demo of selenium framework tool """ def __init__(self): driver = webdriver.Firefox() try: self.driver = driver except Exception: raise NameError("Firefox Not Found!") def clearCookies(self): """ clear all cookies after driver init """ self.driver.delete_all_cookies() def refreshBrowser(self): self.driver.refresh() def maximizeWindow(self): self.driver.maximize_window() def navigate(self, url): self.driver.get(url) def quitBrowser(self): self.driver.quit() def closeBrowser(self): self.driver.close() def getElement(self, selector): """ to locate element by selector :arg selector should be passed by an example with "i,xxx" "x,//*[@id='langs']/button" :returns DOM element """ if ',' not in selector: return self.driver.find_element_by_id(selector) selector_by = selector.split(',')[0] selector_value = selector.split(',')[1] if selector_by == "i" or selector_by == 'id': element = self.driver.find_element_by_id(selector_value) elif selector_by == "n" or selector_by == 'name': element = self.driver.find_element_by_name(selector_value) elif selector_by == "c" or selector_by == 'class_name': element = self.driver.find_element_by_class_name(selector_value) elif selector_by == "l" or selector_by == 'link_text': element = self.driver.find_element_by_link_text(selector_value) elif selector_by == "p" or selector_by == 'partial_link_text': element = self.driver.find_element_by_partial_link_text(selector_value) elif selector_by == "t" or selector_by == 'tag_name': element = self.driver.find_element_by_tag_name(selector_value) elif selector_by == "x" or selector_by == 'xpath': element = self.driver.find_element_by_xpath(selector_value) elif selector_by == "s" or selector_by == 'selector_selector': element = self.driver.find_element_by_css_selector(selector_value) else: raise NameError("Please enter a valid type of targeting elements.") return element def type(self, selector, text): """ Operation input box. Usage: driver.type("i,el","selenium") """ el = self.getElement(selector) el.clear() el.send_keys(text) def click(self, selector): """ It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.click("i,el") """ el = self.getElement(selector) el.click() def selectByIndex(self, selector, index): """ It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.select_by_index("i,el") """ el = self.getElement(selector) Select(el).select_by_index(index) def clickByText(self, text): """ Click the element by the link text Usage: driver.click_text("新闻") """ self.getElement('p,' + text).click() def submit(self, selector): """ Submit the specified form. Usage: driver.submit("i,el") """ el = self.getElement(selector) el.submit() def executeJs(self, script): """ Execute JavaScript scripts. Usage: driver.js("window.scrollTo(200,1000);") """ self.driver.execute_script(script) def getAttribute(self, selector, attribute): """ Gets the value of an element attribute. Usage: driver.get_attribute("i,el","type") """ el = self.getElement(selector) return el.getAttribute(attribute) def getText(self, selector): """ Get element text information. Usage: driver.get_text("i,el") """ el = self.getElement(selector) return el.text def getDisplay(self, selector): """ Gets the element to display,The return result is true or false. Usage: driver.get_display("i,el") """ el = self.getElement(selector) return el.is_displayed() def getTitle(self): ''' Get window title. Usage: driver.get_title() ''' return self.driver.title def getUrl(self): """ Get the URL address of the current page. Usage: driver.get_url() """ return self.driver.current_url def acceptAlert(self): ''' Accept warning box. Usage: driver.accept_alert() ''' self.driver.switch_to.alert.accept() def dismissAlert(self): ''' Dismisses the alert available. Usage: driver.dismissAlert() ''' self.driver.switch_to.alert.dismiss() def implicitlyWait(self, secs): """ Implicitly wait. All elements on the page. Usage: driver.implicitly_wait(10) """ self.driver.implicitly_wait(secs) def switchFrame(self, selector): """ Switch to the specified frame. Usage: driver.switch_to_frame("i,el") """ el = self.getElement(selector) self.driver.switch_to.frame(el) def switchDefaultFrame(self): """ Returns the current form machine form at the next higher level. Corresponding relationship with switch_to_frame () method. Usage: driver.switch_to_frame_out() """ self.driver.switch_to.default_content() def openNewWindow(self, selector): ''' Open the new window and switch the handle to the newly opened window. Usage: driver.open_new_window() ''' original_windows = self.driver.current_window_handle el = self.getElement(selector) el.click() all_handles = self.driver.window_handles for handle in all_handles: if handle != original_windows: self.driver._switch_to.window(handle)
Base Page类
class RanzhiBasePage(): def __init__(self, driver, baseUrl): """ 构造方法 :param driver: 封装好的webdriver :param baseUrl: 然之系统的基本url http://【localhost:808】/ranzhi/www """ self.baseUrl = baseUrl self.driver = driver def openPage(self, url): """ 打开然之系统的页面,通过拼接URL的方式 :param url: /sys/index.html :return: """ self.driver.navigate(self.baseUrl + url)
Sub Page类
from ranzhiWeekend.ranzhi_base_page import RanzhiBasePage class RanzhiSubLoginPage(RanzhiBasePage): def __init__(self, driver, baseUrl): """ :param driver: :param baseUrl: """ # 调用其 基类 RanzhiBasePage的 构造函数 # 实现 基类 的构造函数的功能 super().__init__(driver, baseUrl) self.loginPageUrl = "/sys/user-login.html" self.mainPageUrl = "/sys/index.html" self.driver.clearCookies() def login(self, userName, password): self.openPage(self.loginPageUrl) # self.driver.clearCookies() self.driver.implicitlyWait(5) self.driver.type("account", userName) self.driver.type("password", password) self.driver.click("submit") def getMainPage(self): return self.baseUrl + self.mainPageUrl
Tests Case 类
import unittest from time import sleep from ranzhiWeekend.automate_driver import AutomateDriver from ranzhiWeekend.ranzhi_sub_login_page import RanzhiSubLoginPage """ 1. 导入 unittest 2. 继承 unittest.TestCase 3. 写用例 方法以 test 开头 4. 考虑使用 setUp() 和 tearDown() """ class RanzhiTests(unittest.TestCase): def setUp(self): """ 开始每个测试前的准备事项 :return: """ self.autoDriver = AutomateDriver() self.baseUrl = "http://localhost:808/ranzhi/www" def tearDown(self): """ 结束每个测试后的清理工作 :return: """ self.autoDriver.quitBrowser() def test_ranzhi_login(self): """ 测试用例:测试然之登录 :return: """ # 新建然之的页面对象 loginPage = RanzhiSubLoginPage(self.autoDriver, self.baseUrl) # 利用然之的页面对象进行登录 loginPage.login("admin", "admin") sleep(2) # 断言 是否登录成功 self.assertEqual(loginPage.getMainPage(), self.autoDriver.getUrl(), u"登录失败")
Tests Runner类
import smtplib import unittest from email.header import Header from email.mime.text import MIMEText from ranzhiWeekend import HTMLTestRunner from ranzhiWeekend.ranzhi_tests_0605 import RanzhiTests class RanzhiTestRunner(): def runTest(self): """ 运行测试用例 :return: """ # 声明一个测试套件 suite = unittest.TestSuite() # 添加测试用例到测试套件 suite.addTest(RanzhiTests("test_ranzhi_login")) # 创建一个新的测试结果文件 buf = open("./result.html", "wb") # 声明测试运行的对象 runner = HTMLTestRunner.HTMLTestRunner(stream=buf, title="Ranzhi Test Result", description="Test Case Run Result") # 运行测试,并且将结果生成为HTML runner.run(suite) # 关闭文件输出 buf.close() def sendEmail(self, targetEmail): """ 发送邮件 :param targetEmail: :return: """ # 打开测试报告结果 f = open("./result.html", "rb") # 将测试结果放到邮件的主体中 mailBody = f.read() # 关闭测试结果的文件 f.close() # 声明一个邮件对象,用刚刚得到的邮件主体 msg = MIMEText(mailBody, "html", "utf-8") # 设置邮件的主题 msg["subject"] = Header("Automation Test Result", "utf-8") # 创建一个SMTP服务对象 # simple message transfer protocol # 简单的消息转移协议 smtpMail = smtplib.SMTP() # 连接SMTP的服务器 smtpMail.connect(“***.******.com") # 登录SMTP的服务器 smtpMail.login(“*******@*****.com", “*********") # 使用SMTP的服务器发送邮件 smtpMail.sendmail(“*******@********.com", targetEmail, msg.as_string()) # 退出SMTP对象 smtpMail.quit()
main函数入口
if __name__ == "__main__": # 实例化一个runner runner = RanzhiTestRunner() # 执行测试 runner.runTest() # 发送测试结果 runner.sendEmail(“********@******.com")