【记录自己的实现过程,和结果无关】
初始实现:
# coding:utf-8
"""
author:@zhouxuan
file:conftest.py
"""
from selenium import webdriver
import pytest
from selenium.webdriver.support.wait import WebDriverWait
import time
from selenium.webdriver.remote.webdriver import By
@pytest.fixture(scope='function')
def login_baidu(request):
dr = webdriver.Chrome()
dr.get('http://www.baidu.com')
dr.maximize_window()
time.sleep(4)
WebDriverWait(dr, 2).until(lambda x: x.find_element_by_xpath('//*[@id="u1"]/a[7]').is_displayed())
dr.find_element(By.XPATH,'//*[@id="u1"]/a[7]').click()
"判断默认展示的是扫码登录还是用户名登录"
try:
WebDriverWait(dr, 2).until(lambda x: x.find_element_by_xpath('//p[@title="用户名登录"][not(@style)]').is_displayed())
b=1
except:
b=2 #默认登录方式未扫码
if b==1:
dr.find_element("xpath",'//p[@title="用户名登录"]').click()
dr.find_element(By.NAME,"userName").send_keys('******')
dr.find_element(By.NAME,"password").send_keys('*****')
dr.find_element(By.ID,"TANGRAM__PSP_10__submit").click()
time.sleep(20) #手工识别
print('one')
yield dr
dr.quit()
# def quit():
# print ('run')
# dr.quit()
# request.addfinalizer(quit)
用例脚本test_baidu.py
# coding:utf-8
"""
author:@
"""
import pytest
from selenium.webdriver.remote.webdriver import By
from selenium.webdriver.common.keys import Keys
from test_finance import pri_packaging
import time
class Test_baidu():
def test_search(self,login_baidu):
"""
:param login_baidu: 搜索
:return:
"""
dr=login_baidu
dr.find_element(By.ID,"kw").send_keys('aaa',Keys.ENTER)
def test_setting(self,login_baidu):
"""
:param login_baidu: 搜索设置
:return:
"""
dr=login_baidu
dr.find_element(By.XPATH,'//*[@id="s_usersetting_top"]/span').click() #点击设置
dr.find_element(By.XPATH,'//*[@id="wrapper"]/div[5]/a[1]').click() #搜索设置
pri_packaging.wait_element_appear(dr,value='//*[@id="s1_2"]')
dr.find_element(By.XPATH,'//*[@id="s1_2"]').click() #搜索框提示不显示
time.sleep(5)
if __name__ == '__main__':
pytest.main(['-s','test_baidu.py'])
可以看出新增了py文件pri_packaging,我是用来封装一些常用的方法,这样写起来更加简单
# coding:utf-8
"""
author:zhouxuan
2019/12/30 10:57
PyCharm 封装常用的方法
"""
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.remote.webdriver import By
def wait_element_appear(driver,timeout=10,by=By.XPATH,value=None):
WebDriverWait(driver, timeout, 1,).until(lambda x: x.find_element(by,value).is_displayed())
执行完毕
============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-5.0.0, py-1.5.4, pluggy-0.13.1
rootdir: D:\django_project\p3_tongtool, inifile: pytest.ini
plugins: allure-pytest-2.8.6, html-2.0.1, metadata-1.8.0, rerunfailures-8.0
collected 2 items
test_baidu.py one
.one
.
========================== 2 passed in 73.88 seconds ==========================
可以看出,用例脚本的可读性还是偏差,接下来我们就准备把用例脚本写成类似伪代码,增加可读性
优化思路:
1、增加脚本可读性(为公司维护脚本人员和新手考虑)
2、脚本相关数据和业务分离
3、常用的方法抽离出来,做成公共函数,方便重复调用。
开始重构上面的用例。在顶级目录下面创建文件夹conf config.ini用于存储数据 config.py 系统变量
conf
--config.ini
--config.py
config.ini
[test_baidu]
${var1}=//*[@id="s1_2"]
搜索关键字=aaa
设置=//*[@id="s_usersetting_top"]/span
搜索设置=//*[@id="wrapper"]/div[5]/a[1]
加载搜索框tab=${var1}
搜索框提示设置不显示=${var1}
config.py
import os
ROOT_DIR=os.path.dirname(os.path.dirname(__file__))
# ui对象库config.ini文件所在目录
CONF_PATH = os.path.join(ROOT_DIR, 'conf', 'config.ini')
接下来要解析config.ini,我这里存储的时候,主要是用按键的名称=识别路径来存储的。 结合脚本中的格式是
find_element(By.XPATH,“//*[@id="s_usersetting_top"]/span”).click(),这样,前面的中文就可以是我们的伪代码了,显示在用例中,方便阅读。最后的替换为 click_obj(driver,baidu_config.split_content('设置')) 。具体的转换过程是
1、先封装find_element_by_xpath(“//*[@id="s_usersetting_top"]/span”).click(),
def click_obj(driver,k_list):
by,value=k_list
paraser_by(by)
driver.find_element(eval(by),value).click()
def paraser_by(by):
if by == 'By.XPATH':
by = By.XPATH
elif by == 'By.ID':
by = By.ID
elif by == 'By.NAME':
by = By.NAME
return by
从上面来看,我们就只需要给出一个list 里面包含[By.XPATH,//*[@id="s_usersetting_top"]/span].参考个人的风格,我喜欢用xpath来定位元素,所以默认就直接用了xpath,如果是其他的,就在ini文件中搜索设置=By.ID>>//*[@id="wrapper"]/div[5]/a[1]这样写,通过解析,来识别出来。具体对应的脚本
from configobj import ConfigObj
from conf.config import CONF_PATH
class parseConfig():
def __init__(self,section):
self.file=CONF_PATH
self.section= section
self.configfile=ConfigObj(self.file,encoding='utf-8')
def get_all_sections(self):
#获取所有的section
return self.configfile.sections
def get_all_options(self):
#获取指定的section内容
return self.configfile[self.section]
def split_content(self,option):
#拆解对应的数据
section = self.section
try:
xpath_result = self.configfile[section][option]
if '$' in xpath_result:
xpath_result=self.configfile[section][xpath_result] #对变量${name}解析,替换成真正的值
if '>>' in xpath_result:
return xpath_result.split('>>')
else:
return ['By.XPATH',xpath_result] #默认xpath格式
except Exception as e:
print ('error',e)
其中的split_content就是用来实现上述功能的。这样返回的就是我们需要的XPATH,以及对象元素的xpath了。
最后脚本就变成了
def test_setting(self,login_baidu):
"""
:param login_baidu: 搜索设置
:return:
"""
baidu_config=parseConfig('test_baidu') #实例化解析类
driver=login_baidu
click_obj(driver,baidu_config.split_content('设置'))
click_obj(driver,baidu_config.split_content('搜索设置'))
waiting_obj(driver,baidu_config.split_content('加载搜索框tab'))
click_obj(driver, baidu_config.split_content('搜索框提示设置不显示'))
这样,我们从每个名称就知道具体的操作步骤了,点击设置-点击搜索设置-等待加载tab-设置搜索框不显示。
最后就说道维护。只要业务流程没有变,测试脚本就不需要来修改了,就只需要维护测试的相关数据就可以了。这里就是只需要维护ini文件即可。后续,我们将讲述到一个脚本,对应多组测试数据时候,如果来做参数化和维护的
最后的脚本其实还可以做下优化,把解析ini的放到公共函数(click_obj,write_obj)中去,这样可读性会更加好。下一偏将给出实际案例。