po封装的原则
遵循原则的话会达到更好的效果
-页面封装里不应该包含断言或者测试操作,不然就做不到页面操作和测试操作分离
-唯一可以操作页面中的测试是判断一个元素是否能找到
-不需要封装所有页面操作,用到什么就封装什么
-页面封装也可以使用组建方式.比如导航栏,侧边栏等在多个页面重复出现的,也可以封装成一个组件类,页面使用的时候只需要继承这个组件的类
-当一个操作引发不同的效果,应该使用多个自动化测试用例函数,比如login_failed,login_success
如登录页面操作:
当登录失败时候,会有错误信息返回,这一逻辑都是相同的,所以我们可以把登录失败的数据都存储到测试数据.py文件里,用列表嵌套字典的方式,相当于读取excel返回的结果
登录成功的时候,页面会跳转,不能再用上面登录失败的逻辑,所以要重新使用一个新的测试用例函数,专门用来测试登录成功,会把登录成功的数据存储到py文件,用另外一个变量存储.由于页面已经跳转,断言时候而需要用到的po页面,是home_page,不是login_page
ui自动化测试数据驱动
数据驱动应用的场景是,测试步骤一样,但是数据不一样
在ui自动化测试中,因为页面操作的逻辑不像接口,有很多不同的变化,逻辑不一样,断言不一样,所以不能只用一个测试用例函数进行自动化测试.也就代表测试数据不能只放在一个excel的表单中,如果使用多个表单,读取起来不方便
数据驱动使用分组的方式,把不同的列表数据,驱动到逻辑不同的测试用例函数
fail_data = login_fail
success_data = login_success
class TestLogin:
@pytest.mark.parametrize("case_info",fail_data)
def test_login(self,case_info,login_page):
#获得测试数据
usename = case_info["username"]
password = case_info["password"]
expected = case_info["expected"]
#使用类里的实例方法
login_page.get_url()
#使用登录方法
login_page.login(usename,password)
#获取错误信息
actual = login_page.get_error_tips()
#断言
assert actual == expected
@pytest.mark.parametrize("case_info",success_data)
def test_login_success(self,case_info,login_page,home_page):
#获得测试数据
usename = case_info["username"]
password = case_info["password"]
expected = case_info["expected"]
#使用类里的实例方法
login_page.get_url()
login_page.login(usename,password)
#断言当前页面url是否跳转到预期的页面
assert home_page.is_loaded()
#断言元素的值是否与预期相符
#定位class - avatar,断言title属性的值
assert home_page.get_username() == expected
ui自动化测试断言
ui自动化测试中,因为逻辑不一样,所以断言也有不同,我们常用的断言方式有:
-获取当前页面的错误信息,与预期比较
-获取某个元素的属性值,判断是否与预期相同
-判断页面是否已经加载完成,判断页面是否已经跳转到预期url
因为加载页面由于网络原因不一定马上就能跳转,有可能还停留在之前的页面,所以需要加入显性等待
显性等到后如果url包含跳转后的字符串,即为true
basepage
作用:对selenium 封装浏览器操作进行二次封装,让整个浏览器的操作变得更加容易使用
po只能用在当前的项目,每个项目开始进行ui自动化之前都需要封装各种页面po,而basepage类能用在不同的项目
把浏览器和页面最通用的操作,都封装到basepage类里,这样可以被其他页面对象(po)使用,使其他po里的代码变得简洁
其他页面对象(po)直接继承basepage,后期重点维护basepage 操作
当po页面的方法的参数出现locator时,就代表传入的参数是一个元组,里面是元素定位表达式
在basepage中初始化对象,就不用在po页面上初始化了,每次在测试用例函数里的参数传入夹具之后,夹具的返回值是该po类的实例,而po类继承BasePage类,所以创建实例时候也要传入浏览器对象作为参数
class BasePage:
def __init__(self,browser:Chrome):
self.browser = browser
页面操作封装
-获取当前url
def is_loaded(self,url,timeout=10):
'''判断某个url是否被加载'''
return WebDriverWait(self.browser, timeout).until(when.url_contains(url))
在po页面调用这个方法的时候要用超继承,因为父类有同名的方法
def is_loaded(self):
'''页面已经被加载'''
#超继承,因为父类有同名方法,不然就是自己调用自己
return super().is_loaded(self.url)
-输入框操作
locator代表定位元素的表达式(元组),words代表输入的内容
def send(self,locator,words):
'''输入框操作'''
wait = WebDriverWait(self.browser, timeout=10)
condiction = when.visibility_of_element_located(locator)
input_el = wait.until(condiction)
input_el.send_keys(words)
-清空输入框操作
当出现查找元素的时候,locator需要加*,表示拆分元组,当出现显性等待条件时就不用
def clear(self,locator):
'''清空输入框'''
el = self.browser.find_element(*locator)
el.clear()
在po页面调用
def login(self,username,password):
locator =("name", "account")
# 清空输入框
self.clear(locator)
#输入用户名
self.send(locator,username)
-加载页面
def goto(self,url):
'''加载页面'''
if url.find("https://") != -1:
return self.browser.get(url)
else:
return self.browser.get(host + url)
-文件上传
def upload(self,filename):
# 文件上传
btn = self.browser.find_element(By.XPATH, '//input[@type="file"]')
btn.send_keys(filename)
-获取某个元素的属性值
def get_element_attribute(self,locator,name):
'''获取某个元素的属性'''
#name是属性名
el = self.browser.find_element(*locator)
return el.get_attribute(name)
-设置/修改元素属性
def set_element_attribute(self,locator,value):
'''设置元素属性'''
el = self.browser.find_element(*locator)
#value是想要在传入的值
js = f"arguments[0].value='{value}'"
self.browser.execute_script(js,el)
-获取错误信息
def get_error_tips(self,locator):
'''获取错误信息'''
wait = WebDriverWait(self.browser, timeout=10)
condiction = when.visibility_of_element_located(locator)
el = wait.until(condiction)
return el.text
-切换到iframe
def iframe(self,locator):
'''切换到iframe'''
wait = WebDriverWait(self.browser,timeout=10)
addiction = when.frame_to_be_available_and_switch_to_it(locator)
wait.until(addiction)
-从iframe切换回主页
def switch_to_default(self):
'''从iframe切换回主页面'''
self.browser.switch_to.default_content()
-页面滚动
def scroll_to_bottom(self):
'''页面滚动到底部'''
js = "window.scrollTo(0,document.body.scrollHeight)"
self.browser.execute_script(js)
-元素滚动至可见
def scroll_view(self,locator):
'''滚动到元素可见'''
el = self.browser.find_element(*locator)
js = "arguments[0].scrollIntoView()"
self.browser.execute_script(js,el)
鼠标操作封装
-点击(显性等待)
def click(self,locator):
'''显性等待点击操作,用于按钮'''
wait = WebDriverWait(self.browser, timeout=10)
condition = when.element_to_be_clickable(locator)
el = wait.until(condition)
#点击操作最好用ActionChains
ActionChains(self.browser).click(el).perform()
-点击(普通)
def nomal_click(self,locator):
'''没有显性等待点击,用于点击图标等'''
el = self.browser.find_element(*locator)
ActionChains(self.browser).click(el).perform()
-双击
def double_click(self,locator):
'''双击'''
wait = WebDriverWait(self.browser, timeout=10)
condition = when.element_to_be_clickable(locator)
el = wait.until(condition)
# 点击操作最好用ActionChains
ActionChains(self.browser).double_click(el).perform()
-右击
def right_click(self,locator):
'''右击'''
wait = WebDriverWait(self.browser, timeout=10)
condition = when.element_to_be_clickable(locator)
el = wait.until(condition)
# 点击操作最好用ActionChains
ActionChains(self.browser).context_click(el).perform()
-拖动
def drag_and_drop(self,locator1,locator2):
'''拖拽'''
wait = WebDriverWait(self.browser, timeout=10)
condition = when.element_to_be_clickable(locator1)
el1 = wait.until(condition)
wait = WebDriverWait(self.browser, timeout=10)
condition = when.element_to_be_clickable(locator2)
el2 = wait.until(condition)
ActionChains(self.browser).drag_and_drop(el1,el2).perform()
-悬停
def hover(self,locator):
'''鼠标悬停'''
el = self.browser.find_element(*locator)
ActionChains(self.browser).move_to_element(el).perform()
键盘操作封装
-回车
def enter(self):
'''回车'''
ActionChains(self.browser).send_keys(Keys.ENTER).perform()
-全选
def select_all(self):
'''全选'''
ac = ActionChains(self.browser)
ac.key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL).perform()
-复制
def copy(self):
'''复制'''
ac = ActionChains(self.browser)
ac.key_down(Keys.CONTROL).send_keys("c").key_up(Keys.CONTROL).perform()
-粘贴
def paste(self):
'''粘贴'''
ac = ActionChains(self.browser)
ac.key_down(Keys.CONTROL).send_keys("v").key_up(Keys.CONTROL).perform()