Pom设计模式 自动化测试模型 自动化测试模型概念 自动化测试模型可以看作自动化测试框架与工具设计的思想。自动化测试不仅仅是单纯写脚本运行就可以了,还需要考虑从到如何使脚本运行效率提高,代码复用,参数化等问题。自动化测试模型分为四大类:线性模型,模块化驱动测试,数据驱动,关键字驱动。
线性测试 概念:线性脚本中每个脚本都相互独立,且不会产生其他依赖与调用,其实就是简单模拟用户某个操作流程的脚本。
1 2 3 4 5 6 7 8 9 from selenium import webdriverfrom time import sleepdriver = webdriver.Chrome() driver.get("https://www.baidu.com" ) driver.find_element_by_id("kw" ).send_keys("线性测试" ) driver.find_element_by_id("su" ).click() sleep(2 ) driver.quit()
优点:这种模型的优势就是每个脚本都是完整且独立的。
缺点:
开发成本高,用例之间存在重复的操作。
维护成本高,由于重复的操作,当重复的操作发生改变时,则需要逐一进行脚本的修改。
模块化驱动测试 概念:把重复的操作代码封装为独立的公共模块,当用例执行时需要用到这部分,直接调用即可,这就是模块驱动测试。比如登录系统,退出登录等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from selenium import webdriverclass Login : def login (self, driver ): driver.find_element_by_id("loginName" ).send_keys("student" ) driver.find_element_by_id("pwd" ).send_keys("123456" ) driver.find_element_by_css_selector("input[readonly='readonly']" ).click() def logout (self, driver ): driver.find_element_by_link_text("退出" ).click() driver.quit() if __name__ == '__main__' : driver = webdriver.Chrome() driver.implicitly_wait(10 ) driver.get("http://192.168.68.251/ceshi/front/login.do" ) Login().login(driver) Login().logout(driver)
优点:由于最大限度消除了重复,从而提高了开发效率和提高测试用例的可维护性。
缺点:测试的数据不同,模块化的步骤相同。比如说重复的登录模块,如果登录用户不同,依旧要重复编写登录脚本。
数据驱动测试 概念:将测试中的测试数据和操作分离,数据存放在另外一个文件中单独维护。通过数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。
新建data包,在data包中新建一个test_data.xls
test_data.xls
新建util包,在util包中新建operation_excel.py
operation_excel.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import xlrdclass OperationExcel : def __init__ (self, filename ): self.table = xlrd.open_workbook(filename) def get_data_by_index (self, index=0 ): sheet = self.table.sheet_by_index(index) return self._get_data_info(sheet) def get_data_by_name (self, name ): sheet = self.table.sheet_by_name(name) return self._get_data_info(sheet) def _get_data_info (self, sheet ): keys = sheet.row_values(0 ) rows = sheet.nrows cols = sheet.ncols data_list = [] for row in range (1 , rows): values = [] for col in range (cols): value = self.read_cell(sheet, row, col) values.append(value) ziped = zip (keys, values) data_list.append(dict (ziped)) return data_list def read_cell (self, sheet, row, col ): sheet = self.table.sheet_by_index(0 ) cell_value = sheet.cell_value(row, col) cell_type = sheet.cell_type(row, col) if cell_type == 1 : cell_value = cell_value elif cell_type == 2 and cell_value % 1 == 0 : cell_value = int (cell_value) elif cell_type == 4 : cell_value = True if cell_value == 1 else False return cell_value if __name__ == '__main__' : op_ex = OperationExcel("../data/test_data.xls" ) data_list = op_ex.get_data_by_name("Sheet1" ) print (data_list)
新建case包,在test包中新建test_case.py
test_case.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from selenium import webdriverfrom util.operation_excel import OperationExcelfrom time import sleepclass Login : def login (self, driver, username, password ): driver.find_element_by_id("loginName" ).send_keys(username) driver.find_element_by_id("pwd" ).send_keys(password) driver.find_element_by_css_selector("input[readonly='readonly']" ).click() def logout (self, driver ): if "学生" in driver.title: driver.find_element_by_link_text("退出" ).click() sleep(2 ) driver.quit() else : sleep(2 ) driver.quit() if __name__ == '__main__' : op_ex = OperationExcel("../data/test_data.xls" ) data_list = op_ex.get_data_by_name("Sheet1" ) for data in data_list: driver = webdriver.Chrome() driver.implicitly_wait(10 ) driver.get("http://192.168.68.251/ceshi/front/login.do" ) Login().login(driver, data['username' ], data['password' ]) Login().logout(driver)
优点: 通过这种方式,将数据和重复操作分开,可以快速增加相似测试,完成不同数据情况下的测试。
关键字驱动测试 概念:将原本的自动化代码进行代码与数据的分离,再将分离出来的代码二次分离,形成所谓行为代码与测试代码,而后再经由数据内容驱动行为代码,生成测试代码。从而更加便捷地进行自动化测试代码的管理,以及提高自动化的复用性,让使用者更加容易理解和使用自动化 。
结构设计:
逻辑代码的实现,本身不存在任何价值,需要结合业务才能够体现作用。
只有测试代码才可以对系统的功能进行自动化测试。
数据与代码进行分离,但凡数据需要改动,直接修改数据文件即可,不会影响到原有代码的稳定性。
新建keys包,在keys包中新建key_word.py
用于对selenium进行二次封装为关键字类
key_word.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from time import sleepfrom selenium import webdriverdef open_browser (type_ ): try : driver = getattr (webdriver, type_)() except : driver = webdriver.Chrome() return driver class Key_Word : def __init__ (self, type_ ): self.driver = open_browser(type_) def open (self, url ): self.driver.get(url) def locator (self, name, value, **kwargs ): return self.driver.find_element(name, value) def input (self, kwargs ): self.locator(**kwargs).send_keys(kwargs['txt' ]) def click (self, kwargs ): self.locator(**kwargs).click() def wait (self, time ): sleep(time) def quit (self ): self.driver.quit()
新建case包,在case包中新建test_case.py
用于编写测试用例
test_case.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from keys.key_wrod import Key_Word import unittestfrom ddt import ddt, file_data@ddt() class Test_baidu (unittest.TestCase): def setUp (self ) -> None : pass @file_data("../data/test_data.yml" ) def test_01 (self, **kwargs ): self.driver = Key_Word(kwargs['type_' ]) self.driver.open (kwargs['url' ]) self.driver.input (kwargs['input' ]) self.driver.click(kwargs['button' ]) self.driver.wait(kwargs['time' ]) self.driver.quit() def tearDown (self ) -> None : pass if __name__ == '__main__' : unittest.main()
新建data包,在data包中新建test_data.yml
用于创建测试数据
test_data.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - type_: Chrome url: https://www.baidu.com input: name: id value: kw txt: 关键字驱动 button: name: id value: su time: 3 - type_: Chrome url: https://www.baidu.com input: name: id value: kw txt: 测试数据 button: name: id value: su time: 5
pom设计模式 pom设计模式概念 POM模式 —-(page object model)页面自动化模型
在自动化中已经流行起来的一种易于维护和减少代码的设计模式。在自动化测试中,PO对象作为一个与页面交互的接口。测试中需要与页面的UI进行交互时,便调用PO的方法。这样做的好处是,如果页面的UI发生了更改,那么测试用例本身不需要更改,只需更改PO中的代码即可。
优点:
测试代码与页面的定位代码(如定位器或者其他的映射)相分离。
该页面提供的方法或元素在一个独立的类中,而不是将这些方法或元素分散在整个测试中。
将页面分成三层:
核心架构 —- 组成结构:
asset–项目名称
common文件夹 —- 放置公共方法
base.py —- 对selenium做二次封装
1.打开浏览器
2.输入地址
3.元素定位
4.点击
5.输入
6.判断类型方法
其他公共方法
page文件夹 — 存放页面
1.一个页面编写一个.py文件,例如:login_page.py
2.page中封装页面表现层和操作层
3.page中的.py文件继承base.py
script文件夹 —存放测试用例
1.封装页面业务层
2.test_login.py
data文件夹—存放测试数据
report文件夹—存放测试报告
pom设计模式实战 项目结构:
新建common包,common包中新建base.py
对selenium进行二次封装
base.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 from selenium import webdriverfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.select import Selectfrom time import sleepdef open_browser (browser ): try : driver_ = getattr (webdriver, browser)() except : driver_ = None print ("浏览器输入不合法!!!" ) return driver_ class Base : def __init__ (self, browser='Chrome' ): self.driver = open_browser(browser) self.driver.maximize_window() def open_url (self, url ): self.driver.get(url) def find_element (self, locator, timeout=10 ): try : element = WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator)) except : element = None print (f"元素{locator} 没找到!!" ) return element def find_elements (self, locator, timeout=10 ): try : elements = WebDriverWait(self.driver, timeout).until(EC.presence_of_all_elements_located(locator)) except : elements = None print (f"元素{locator} 没找到!!" ) return elements def send_keys (self, locator, text ): element = self.find_element(locator) element.clear() element.send_keys(text) def click (self, locator ): self.find_element(locator).click() def select (self, locator ): element = self.find_element(locator) return Select(element) def select_by_index (self, locator, index ): self.select(locator).select_by_index(index) def select_by_value (self, locator, value ): self.select(locator).select_by_value(value) def select_by_text (self, locator, text ): self.select(locator).select_by_visible_text(text) def alert (self ): return self.driver.switch_to.alert def alert_close (self, bool_=True ): if bool_: self.alert().accept() else : self.alert().dismiss() def alert_text (self ): return self.alert().text def alert_send_keys (self, value ): self.alert().send_keys(value) def is_text_in_element (self, locator, text, timeout=10 ): result = WebDriverWait(self.driver, timeout).until(EC.text_to_be_present_in_element(locator, text)) return result def is_value_in_element (self, locator, value, timeout=10 ): result = WebDriverWait(self.driver, timeout).until(EC.text_to_be_present_in_element_value(locator, value)) return result def is_selected (self ): result = self.driver.is_selected() return result def title (self ): return self.driver.title def sleep (self, time ): sleep(time) def quit (self ): self.driver.quit()
新建page包,在page包中新建login_page.py
用于封装页面的表现层和操作层(继承Base类)
login_page.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 """ login_page.py 封装页面的表现层 封装页面的操作层 需要继承Base类 """ from common.base import Baselogin_url = "http://192.168.68.251" class LoginPage (Base ): """封装表现层:制作定位器""" login_name_loc = ("id" , "loginName" ) password_loc = ("id" , "pwd" ) forget_password_loc = ("link text" , "忘记密码?" ) login_btn_loc = ("css selector" , "input[readonly]" ) """ 封装操作层:元素操作 每一个元素操作都写成一个方法 """ def input_login_name (self, username ): self.send_keys(self.login_name_loc, username) def input_password (self, password ): self.send_keys(self.password_loc, password) def click_login_btn (self ): self.click(self.login_btn_loc)
新建script包,在script包中新建login_test.py
用于封装业务层(测试用例)
login_test.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 """ login_test.py 封装业务层 """ from page.login_page import LoginPage, login_urlimport unittestfrom ddt import ddt, file_data, data, unpack@ddt() class TestLogin (unittest.TestCase): def setUp (self ) -> None : self.login_page = LoginPage() self.login_page.open_url(login_url) @data(("student" , "123456" ), ("teacher" , "19216868251" ) ) @unpack def test_login_data (self, username, password ): self.login_page.input_login_name(username) self.login_page.input_password(password) self.login_page.click_login_btn() self.assertIn("任务列表" , self.login_page.title(), msg="登录失败!!!" ) @file_data("../data/login_data.yml" ) def test_login_file_data (self, username, password ): self.login_page.input_login_name(username) self.login_page.input_password(password) self.login_page.click_login_btn() self.assertIn("任务列表" , self.login_page.title(), msg="登录失败!!!" ) def tearDown (self ) -> None : self.login_page.quit() if __name__ == '__main__' : unittest.main()
新建data包,在data包中新建login_data.yml
用于定义测试数据
login_data.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - username: student password: 123456 - username: '' password: 123456 - username: student1 password: 123456 - username: STUDENT password: 123456 - username: student password: '' - username: student password: 111111
新建report包,在report包中新建run_login_html.py
用于执行测试用例生成测试报告并存放
run_login_html.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import unittestimport timeimport HTMLTestRunnertest_dir = "../script" discover = unittest.defaultTestLoader.discover(test_dir, pattern='*test.py' ) report_dir = "../report" now_time = time.strftime("%Y-%m-%d %H-%M-%S" ) report_filename = report_dir + "/" + now_time + "_report.html" stream = open (report_filename, "wb" ) runner = HTMLTestRunner.HTMLTestRunner(stream=stream, verbosity=2 , title="登录模块测试" , description="登录测试执行结果" ) runner.run(discover) stream.close()
html报告结果: