python_pytest-软件测试UI自动化全集【1】
前言
其实一直不想开篇UI自动化,实在是。。怎么说呢,实际工作中ROI极低(仅针对我目前的工作)最后我还是决定分享出来,毕竟互联网行业现象嘈嘈切切、五光十色,总有一些项目、或者一些模块功能,就是十分稳定,及其适合拎出来加入Ui自动化的代办列表中。且,为了给对自动化技术感兴趣的盆友一些来自真实样本的现身说法。于是乎,有了以下~
项目的选取
该项目算我第一次比较全面,系统地建立一个UI自动化的测试框架,真真实现了我所在公司的UI自动化从0到1的突破。入选的项目必定要满足:1需求变动不频繁2项目周期长3项目需要回归测试。如不符合以上3点,那UI自动化的投入产出比必定大打折扣。
自动化项目是在功能测试完毕后开始的。是的,并非开发完就开始编写自动化代码。规范且应该的流程就是在手工测试完成后、项目趋于稳定时开始的。
UI自动化用例设计的原则
-
自动化测试用例一般只实现核心业务流程或者重复执行率较高的功能
- 这是因为UI自动化一般就是用于回归测试的
-
自动化测试用例的选择一般以“正向”逻辑的验证为主
- 这是因为UI自动化一般用于验证主要功能、主流程的
-
不是所有手工用例都可以使用自动化测试来执行。
- 如上,说实话,有些功能点就是手工比自动化效率高,且高不少。因为自动化的前置条件多且繁琐。
-
尽量减少多个用例脚本之间的依赖。
- 减少耦合,必定能增加脚本的稳定性。尽量做到每个方法单拎出来都可以单独执行的。
-
自动化测试用例执行完毕之后,一般需要回归原点。
- 这是为了重复利用我们的脚本,重复执行的前提是无需人工参加调整。如查询列表,要保证搜索框能搜索出对应的内容,那前提就是列表的数据应该是稳定的。
框架
从框架的选取入手,挑选合适做自动化测试的功能,设计对应的测试用例。我选择的pytest框架,它是 python 的一种单元测试框架,同自带的 UnitTest 测试框架类似,相比于 UnitTest 框架,使用起来更简洁,效率更高。它的支持参数化、跳过、重复执行,灵活支持第三方插件等功能牢牢地抓住了我。下面从我实战项目的框架架构开始
项目搭建
目录结构
初始化代码
封装自定义工具类
utils.py
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class UtilsDriver:
_driver=None
@classmethod
#classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,可以直接来调用类的属性,类的方法等。
def get_driver(cls):#获取浏览器驱动-以谷歌为例
if cls._driver is None:
cls._driver=webdriver.Chrome()#谷歌
# cls._driver=webdriver.Firefox()#火狐
cls._driver.maximize_window()
cls._driver.implicitly_wait(5)
cls._driver.get("https://****.com.cn/#/login?")
time.sleep(1)
return cls._driver
@classmethod
def quit_driver(cls):#关闭浏览器驱动
if cls._driver is not None:
cls.get_driver().quit()
cls._driver=None
# @classmethod
# def Wait_ele(cls,driver,time,pat):
# return WebDriverWait(driver, time, 1).until(lambda x: x.find_element(By.XPATH,pat))
@classmethod#返回获取到的界面弹窗文字
def get_mes(cls, xpath):
return UtilsDriver.get_driver().find_element(By.XPATH, xpath).text
@classmethod#返回找到的指定元素
def get_element_1(self,xpath):
"""
:param xpath: 表示元素定位的路径
:return: 返回找到的元素
"""
return self.driver.find_element(By.XPATH, xpath)
@classmethod#返回获取到的界面元素
def get_attribute(cls, xpath, attribute):
return UtilsDriver.get_driver().find_element(By.XPATH, xpath).get_attribute(attribute)
@classmethod
def get_alert(cls):#返回获取到的警告弹窗
WebDriverWait(cls.driver, timeout=10, poll_frequency=0.5).until(expected_conditions.alert_is_present())
#wait.until(expected_conditions.alert_is_present())
alert = cls.driver.switch_to.alert
return alert.text
@classmethod#每次执行完返回项目首页,实现原则5(自动化测试用例执行完毕之后,一般需要回归原点。)
def return_home(cls):
# self.Return_home=By.XPATH,"//*/div[@class='nav-container']/div/div[1]"#撤回成功后返回首页
return WebDriverWait(UtilsDriver.get_driver(), 10, 1).until(lambda x: x.find_element(By.XPATH,"//*/div[@class='nav-container']/div/div[1]"))
封装po基类
base_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from ALXQ.utils import UtilsDriver
class BasePage:
def __init__(self):#在对象创建时需要执行的初始化操作,则每次实例化BasePage都会先获取浏览器驱动的方法
print("基类的~BasePage")
self.driver=UtilsDriver.get_driver()#get_driver方法的引用就有隐形等待
def get_element(self,location):#page页对象层的基类,显式等待
wait=WebDriverWait(self.driver,10,1)
element=wait.until(lambda x:x.find_element(*location))
return element
def get_element_1(self,xpath):#返回按XPATH路径寻找到的元素
"""
:param xpath: 表示元素定位的路径
:return: 返回找到的元素
"""
return self.driver.find_element(By.XPATH, xpath)
def get_elements(self,xpath):#返回按XPATH路径寻找到的元素们
"""
:param xpath: 表示元素定位的路径
:return: 返回找到的元素
"""
return self.driver.find_elements(By.XPATH,xpath)
class BaseHandle:
def input_text(self,element,text):#对指定元素进行操作
'''
:param element: 表示元素得对象
:param text: 表示要输入的内容
:return:
'''
element.clear()#一般用于清除输入框默认文字
element.send_keys(text)#对输入框进行文字输入
pytest.ini:管理测试脚本,如指定执行的路径
代码编写
1.抽取PO
根据用例分析待测功能,提取页面对象
1.1定义页面对象文件
如,登录页:page_login.py
就是用于对登录这个功能及相关流程进行操作的代码编写
如,添加企业:page_add_campany.py
就是用于对添加企业这个功能及相关流程进行操作的代码编写
1.2PO分层封装
以登录功能为例的完整代码编写
'''
登录功能
'''
import time
from selenium.webdriver.common.by import By
from ALXQ.utils import UtilsDriver
from ALXQ.base.base_page import BasePage
import ddddocr
from pykeyboard import PyKeyboard
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains
#from selenium.webdriver.support.wait import WebDriverWait
# from pymouse import PyMouse
# import Pyhook
# import pywin32
class PageLogin(BasePage):#对象库层,主要为找出对应元素
def __init__(self):
super().__init__()
self.password_login_bt2=By.XPATH,"//div[text()='密码登录']"
self.username=By.XPATH,"//*/input[@placeholder='请输入手机号']"
self.password2=By.XPATH,"//*/div[@class='password-box']"
self.choose=By.XPATH,"//*/span[@class='el-checkbox__inner']"
self.login_bt=By.XPATH,"//*[text()='登录']"
self.code=By.XPATH,"(//div/div/img)[5]"#识别图像验证码的图
self.code_input=By.XPATH,"//*/input[@placeholder='图形验证码']"#识别图形验证码的输入框
self.alert=By.XPATH,"//*/div[@role='alert']"#判断有弹窗
self.code_login=By.XPATH,"//*[text()='验证码登录']"#验证码登录元素
self.pwd_login=By.XPATH,"//*[text()='密码登录']"#密码登录元素
def find_password_login_bt2(self):#找到账号密码登录的按钮
return self.get_element(self.password_login_bt2)
def find_pwd(self):
return self.get_element(self.pwd)
def find_password2(self):
return self.get_element(self.password2)
def find_username(self):
return self.get_element(self.username)
def find_choose(self):
return self.get_element(self.choose)
def find_login_bt(self):
return self.get_element(self.login_bt)
def find_code(self):
return self.get_element(self.code)
def find_code_input(self):
return self.get_element(self.code_input)
def find_alert(self):#找是否有弹窗
return self.get_element(self.alert)
def find_code_login(self):#找验证码登陆
return self.get_element(self.code_login)
def find_pwd_login(self):#找密码登录
return self.get_element(self.pwd_login)
class HandleLogin:#操作层:对对象库进行操作
def __init__(self):
self.driver = UtilsDriver.get_driver()
self.login_page=PageLogin()
# self.m=PyMouse()#实例化鼠标对象
self.k = PyKeyboard()#实例化键盘对象
self.keys = Keys()
self.ac = ActionChains(self.driver)
def click_find_password_login_bt2(self):#找到密码登录,对密码登录按钮进行点击
self.login_page.find_password_login_bt2().click()
time.sleep(1)
def input_username(self, username):#输入用户名
self.login_page.find_username().clear()
time.sleep(1)
self.login_page.find_username().send_keys(username)
def input_password2(self, password2):
self.login_page.find_password2().click()
for i in range(10):
self.login_page.find_password2().send_keys(Keys.BACK_SPACE)#无法使用clear,只能点10次BACK_SPACE
self.login_page.find_password2().send_keys(password2)
def click_choose_bt(self):
self.login_page.find_choose().click()
def click_login_bt(self):
self.login_page.find_login_bt().click()
# time.sleep(2)
def input_code(self):#识别图像验证码
ocr=ddddocr.DdddOcr()#实例化ddddocr
code_see=ocr.classification(self.login_page.find_code().screenshot_as_png)#识别出验证码
# self.login_page.find_code_input().clear()
self.login_page.find_code_input().clear()
self.login_page.find_code_input().send_keys(code_see)
def login_fail(self):#登录失败,有弹窗,点验证码登录,再点密码登录
if self.login_page.find_alert:
self.login_page.find_code_login().click()
time.sleep(1)
self.login_page.find_pwd_login().click()
time.sleep(1)
class LoginProxy:#业务层:将操作层关联起来,形成一个登录功能的正向操作流程(如1点击账号密码登录-2输入用户名-3输入密码-4输入验证码-5勾选协议-6点击登录的按钮)
def __init__(self):
self.handle_login=HandleLogin()
def login(self,username,password2):
# self.handle_login.click_find_password_login_bt()
time.sleep(1)
self.handle_login.click_find_password_login_bt2()
print("找到用密码进行登录的选项")
self.handle_login.input_username(username)
print("#输入用户名")
self.handle_login.input_password2(password2)
print("#输入密码")
time.sleep(1)
self.handle_login.input_code()
print("#输入验证码")
self.handle_login.click_choose_bt()
print("#勾选协议")
self.handle_login.click_login_bt()
print("#点击登录")
后记
自动化技术是旷野,而非轨道。目前有多种方式可以实现自动化测试,且以后会有更多,不一定非要UI自动化。合适项目的,才是最好的技术!