selenium某些元素定位+unittest、pytest用法、allure
1、selenium4.4的版本没有find_element_by_id等用法了 换成了find_element和find_elements 测试本地文件: driver=webdriver.Chrome() path=os.path.dirname(os.path.abspath(__file__)) #如果还有上层,再套一层os.path.dirname() file_path='file:///'+path+'/xx.html' driver.get(file_path) 复选框选中示例:checkbox
#is_selected是否被选中,返回true选中 male=driver.find_element(By.NAME,'male') if not male.is_selected(): male.click() 单选框选中示例:radio,具有排他性,只能执行一个个选,操作方法与复选框类似 下拉列表操作
from selenium.webdriver.support.select impot Select #下拉单单选 se=driver.find_element(By.id,'idname') sel=Select(se) sel.select_by_index(2) #第三个 time.sleep(2) sel.select_by_value('bj') time.sleep(2) sel.select_by_visible_text('shanghai') #下拉框多选 for i in range(3): sel.select_by_index(i) time.sleep(1) time.sleep(3) #反选,选中多个后取消选中 sel.deselect_all() time.sleep(3) #打印下拉框所有选项值 for option in sel.options: print(option) 弹窗处理、用法 1、alert 用来提示 2、confirm 用来确认 3、prompt 输入内容 #方法属性 1、accept() 接受 2、dismiss() 取消 3、text 显示的文本 4、send_keys 输入内容 #alert处理 driver.find_element(By.ID,'alert').click() #切换到alert alert=driver.switch_to.alert print(alert.text) time.sleep(3) alert.accept() #confirm处理 driver.find_element(By.ID,'confirm').click() confirm=driver.switch_to.alert print(confirm.text) time.sleep(3) confirm.accept() #确认 confirm.dismiss() #取消 #prompt处理 driver.find_element(By.ID,'prompt').click() time.sleep(2) prompt=driver.switch_to.alert print(prompt.text) time.sleep(2) prompt.accept() time.sleep(2) selenium三种等待方式 1、time.sleep,调试的时候可以用,但在实际开发自动化框架中不建议用 2、implicitly_wait隐式等待,全局有效,最长等待时间,在规定时间内网页加载完成则执行下一步, 否则一直等到时间结束,然后执行下一步。只要设置一次就可以 3、WebDriverWait显示等待,等待单个的元素加载,通常配合until()、until_not()方法使用。 from selenium.webdriver.support.wait impo WebDriverWait WebDriverWait的参数: WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None) driver:浏览器驱动 timeout:最长超时时间,默认以秒为单位 poll_frequency:检测的间隔步长,默认为0.5s ignored_exceptions:超时后的抛出的异常信息,默认抛出NoSuchElementExeception异常。 这个模块一共只有2个方法until、until_not: method: 在等待期间,每隔一段时间(__init__中的poll_frequency)调用这个传入的方法,直到返回值不是False message: 如果超时,抛出TimeoutException,将message传入异常 示例: time.sleep #线程阻塞blocking wait driver.implicitly_wait(10) #最长10s找不到进入下一步 from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait impo WebDriverWait wait=WebDriverWait(driver,5) #5s超时 wait.until(EC.title_is('百度一下')) #找到了title再进行下一步 Selenium等待条件
from selenium.webdriver.support import expected_conditions as EC 例如: wait=WebDriverWait(driver,5) #5s超时 wait.until(EC.text_to_be_present_in_element(By.id,'id1'),'id 1') print(driver.find_element(By.ID,'id 1').text) print('ok') 鼠标和键盘事件 右击,双击,拖动等操作,这些操作包含在ActionChains类 #正确使用方法 ActionChains(driver).click(btn).perfmon()下面列出的ActionChains常用方法
示例: from selenium.webdriver import ActionChains, Keys driver.get('https://sahitest.com/demo/clicks.htm') #双击 btn=driver.find_element(By.XPATH,'/html/body/form/input[2]') ActionChains(driver).double_click(btn).perform() #单击 btn=driver.find_element(By.XPATH,'/html/body/form/input[3]') ActionChains(driver).click(btn).perform() #右击 btn=driver.find_element(By.XPATH,'/html/body/form/input[4]') ActionChains(driver).context_click(btn).perform() #键盘 driver.get('https://www.baidu.com') kw=driver.find_element(By.ID,'kw') kw.send_keys('selenium') #复制、剪切、粘贴 kw.send_keys(Keys.CONTROL,'a') kw.send_keys(Keys.CONTROL,'x') kw.send_keys(Keys.CONTROL,'v') #鼠标移到某个元素 e=driver.find_element(By.ID,'tj_login') ActionChains(driver).move_to_element(e).click(e).perform() selenium执行js脚本 有两个方法,分别是: 1、execute_script 同步执行 2、execute_async_script 异步执行 通过js通常可以实现页面滚动 #执行js方法和技巧 #加js弹出框 driver.get('https://www.baidu.com') driver.execute_script('alert'('test')) sleep(2) driver.switch_to.alert.accept() #打印页面title js='return document.title' title=driver.execute_script(js) print(title) #变更输入框颜色 driver.find_element(By.ID).send_keys('selenium') driver.find_element(By.ID).click() sleep(2) js='var a=document.getElementById("ke");q.style.border="2px solid red"' driver.execute_script(js) sleep(2) selenium屏幕截屏
#保存图片 st= time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time())) file_name=st+'.png' #按日期命名图片 driver.save_screenshot(file_name) #保存到文件夹 path=os.path.abspath('screenshot') file_path=path+'/'+file_name driver.get_screenshot_as_file(file_path) selenium定位frame、iframe frame标签有frameset、frame、iframe三种,frameset跟其他普通标签没有区别,不会影响到正常的定位,而frame 与iframe对selenium定位而言是一样的,selenium有一组方法对frame进行操作
#frame定位 driver.get('https://sahitest.com/demo/framesTest.htm') top=driver.find_element(By.NAME,'top') driver.switch_to.frame(top) driver.find_element(By.XPATH,'/html/body/table/tbody/tr/td[1]/a[1]').click() sleep(3) #找第二个frame #跳出,再切到另一个frame里去操作 driver.switch_to.default_content() second=driver.find_element(By.XPATH,'/html/frameset/frame[2]') driver.find_element(By.XPATH,'/html/body/table/tbody/tr/td[1]/a[2]').click() 协议勾选定位不到用PyAutoGUI组件通过坐标定位 pip install pyautogui
import pyautogui from selenium.webdriver.common.by import By def test1(): driver=webdriver.Chrome() driver.get('https://testerhome.com/account/sign_up') driver.maximize_window() sleep(1) elem=driver.find_element(By.CLASS_NAME,'custom-control-label') r1=elem.rect pyautogui.moveTo(r1['x']+10,r1['y']+130) pyautogui.click() sleep(3) Selenium断言方法 断言方法: assertEqual(a, b, [msg='测试失败时打印的信息']) # a == b 断言a和b是否相等,相等则测试用例通过 assertNotEqual(a,b,[msg='测试失败时打印的信息']) # a != b 断言a和b是否相等,不相等则测试用例通过 assertTrue(x,[msg='测试失败时打印的信息']) # x is True 断言x是否True,是True则测试用例通过 assertFalse(x,[msg='测试失败时打印的信息']) # x is False 断言x是否False,是False则测试用例通过 assertIn(a,b,[msg='测试失败时打印的信息']) # a in b 断言a是否在b中,在b中则测试用例通过 assertNotIn(a,b,[msg='测试失败时打印的信息']) # a not in b 断言a是否在b中,不在b中则测试用例通过 assertIsNone(x,[msg='测试失败时打印的信息']) # x is None 断言x是否None,是None则测试用例通过 assertIsNotNone(x,[msg='测试失败时打印的信息']) # x not is None 断言x是否None,不是None则测试用例通过 assertIs(a,b,[msg='测试失败时打印的信息']) # a is b 断言a是否是b,是则测试用例通过 assertNotIs(a,b,[msg='测试失败时打印的信息']) # a not is b 断言a是否是b,不是则测试用例通过 assertIsInstance(a,b,[msg='测试失败时打印的信息']) # 断言a是是b的一个实例,是则测试用例通过 assertNotIsInstance(a,b,[msg='测试失败时打印的信息']) # 断言a是是b的一个实例,不是则测试用例通过 selenium获取文本的方法 element=driver.find_element(By.XPATH, '//*[@class="el-form-item__error"]')]) 1.element.text 2.get_attribute(“textContent”) 优点:可以获取隐藏元素的文本 缺点:IE不支持;获取有些元素的文本时,结果中带有空字符串;(没有尝试过) 3.get_attribute("innerText") 优点:可以获取隐藏元素的文本 缺点:FireFox不支持;(每个博客能搜到的都说这个缺点,但我实际操作发现可以获取到每次所需的文本) 示例: from selenium.webdriver.common.by import By driver.find_element(By.XPATH,'//button[text()="Some Text"]') driver.find_elements(By.XPATH,'//button') 2、元素重复时,找上一层即父类的class元素 username_el1=driver.find_elements(By.CLASS_NAME,'form-group')[1] #先找父类的元素,父类元素在第2个值即1 username_el2=username_el1.find_element(By.CLASS_NAME,'form-control') #再找父类下的元素,默认取第一个值0 username_el2.send_keys('css') #最后发送值 3、#找元素返回true或false,3s找不到就关闭 locate=(By.CLASS_NAME,'form-group') WebDriverWait(driver,3).until(EC.visibility_of_element_located(locate)) driver.close() 4、打印文本框的值,输出文本框的值和send_keys的值 username_ele=driver.find_element(By.ID,'user_name') #文本框的默认值 print(username_ele.get_attribute('placeholder')) username_ele.send_keys('css') #打印文本框的值,默认用value字段 print(username_ele.get_attribute('value')) 5、随机生成用户名、邮箱等信息 import random for i in range(5): #random.sample('123456sjsldhh',5)生成的是列表,要转成字符串 user_login=''.join(random.sample('123456sjsldhh',5)) user_email=''.join(random.sample('123456sjsldhh',5))+'@123.com' # print(user_login) # print(user_email) #调用随机生成的用户名和邮箱 driver.find_element(By.ID,'user_login').send_keys(user_login) driver.find_element(By.ID,'user_email').send_keys(user_email) 6、解决验证码 思路:保存验证码图片后,用工具解析 安装库:pip install Pillow、pip install pytesseract from PIL import Image #1、先将整张图保存,命名 driver.save_screenshot('E:\img\wyt.png') #2、定位到验证码元素 code_ele=driver.find_element(By.CLASS_NAME,'rucaptcha-image') #3、打印验证码元素位置坐标 print(code_ele.location) #{'x': 338, 'y': 721} #4、获取验证码的坐标 left=code_ele.location['x'] top=code_ele.location['y'] right=code_ele.size['width']+left height=code_ele.size['height']+top #5、保存截取的坐标图 im=Image.open('E:\img\wyt.png') img=im.crop((left,top,right,height)) img.save('E:\img\wyt1.png') #6、解析验证码图片,只能解析有规则的验证码,带下划线等干扰的验证码不能识别 #coding=utf-8 import pytesseract from PIL import Image image=Image.open('E:\img\wyt1.png') code_text=pytesseract.image_to_string(image) print(code_text) #7、识别有干扰的验证码 借用第三方做好的识别api调用,需要付费 #8、muggle-ocr 识别验证码库 安装库:pip install muggle-ocr -i https://mirrors.aliyun.com/pypi/simple/ 说明 1、muggle_ocr是一款轻量级的ocr识别库,对于python来说是识别率较高的图片验证码模块。 2、主要用于识别各种类型的验证码,一般文字提取效果稍差。 实例: import muggle_ocr # 初始化sdk;model_type 包含了 ModelType.OCR/ModelType.Captcha 两种模式,分别对应常规图片与验证码 sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.Captcha) with open(r"d:\Desktop\四位验证码.png", "rb") as f: img = f.read() text = sdk.predict(image_bytes=img) print(text) 相关实例扩展: import time # 1. 导入包 import muggle_ocr """ 使用预置模型,预置模型包含了[ModelType.OCR, ModelType.Captcha] 两种 其中 ModelType.OCR 用于识别普通印刷文本, ModelType.Captcha 用于识别4-6位简单英数验证码 """ # 打开印刷文本图片 with open(r"test1.png", "rb") as f: ocr_bytes = f.read() # 打开验证码图片 with open(r"test2.jpg", "rb") as f: captcha_bytes = f.read() # 2. 初始化;model_type 可选: [ModelType.OCR, ModelType.Captcha] sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.OCR) # ModelType.Captcha 可识别光学印刷文本 for i in range(5): st = time.time() # 3. 调用预测函数 text = sdk.predict(image_bytes=ocr_bytes) print(text, time.time() - st) # ModelType.Captcha 可识别4-6位验证码 sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.Captcha) for i in range(5): st = time.time() # 3. 调用预测函数 text = sdk.predict(image_bytes=captcha_bytes) print(text, time.time() - st) """ 使用自定义模型 支持基于 https://github.com/kerlomz/captcha_trainer 框架训练的模型 训练完成后,进入导出编译模型的[out]路径下, 把[graph]路径下的pb模型和[model]下的yaml配置文件放到同一路径下。 将 conf_path 参数指定为 yaml配置文件 的绝对或项目相对路径即可,其他步骤一致,如下示例: """ with open(r"test3.jpg", "rb") as f: b = f.read() sdk = muggle_ocr.SDK(conf_path="./ocr.yaml") text = sdk.predict(image_bytes=b) uinttest的加载方法 # 1、通过测试用例类加载、测试套件 suite.addTests(loader.loadTestsFromTestCase(TestUnittest)) suite.addTests(loader.loadTestsFromTestCase(TestUnittest02)) #2、通过测试用例模板加载 suite.addTest(loader.loadTestsFromModule(TestUnittest02)) #3、通过路劲加载,推荐 import os path=os.path.dirname(os.path.abspath(__file__)) suite.addTest(loader.discover(path)) runner=unittest.TextTestRunner() runner.run(suite) pytest编写规则 1、测试文件以test开头(以test结尾也可以) 2、测试类以Test开头,并且不能带有init方法 3、测试函数以test开头 4、断言使用基本的assert即可



import pytest class TestCase02(object): @pytest.mark.do def test01(self): print('测试1') self.add() #pytest函数命名要以test开头才会执行,或者其他函数调用执行 def add(self): print('增加') @pytest.mark.undo def test02(self): print('测试2') if __name__=='__main__': pytest.main(['test02.py'])
import pytest '''pytest参数化处理: 使用参数化装饰器、解析列表、元组、字典等数据 ''' #列表 data=['123','456'] @pytest.mark.parametrize('pwd',data) def test1(pwd): print(pwd) #元组 data2=[ (1,2,3), #或者[1,2,3] (4,5,6) #或者[4,5,6] ] @pytest.mark.parametrize('a,b,c',data2) def test2(a,b,c): print(a,b,c) #字典 data3=( { 'user':1, 'pwd':2 }, { 'age':1, 'email':'111@dd.com' }) @pytest.mark.parametrize('dic',data3) def test3(dic): print(dic) data_1=[ #id的值可以自定义,方便理解每个用例是干什么的即可 pytest.param(1,2,3,id='(a+b):pass'), pytest.param(4,5,10,id='(a+b):fail') ] def add(a,b): return a+b class TestParametrize(object): @pytest.mark.parametrize('a,b,expect',data_1) def test_parametrize_1(self,a,b,expect): assert add(a,b) == expect
import pytest @pytest.fixture() def init(): print('测试init') return 1 def test1(init): print('测试1') def test2(init): print('测试2') if __name__=='__main__': pytest.main(['-sv','test04.py'])
import pytest class TestCase(object): @classmethod def setup_class(cls): print('setup-class') @classmethod def teardown_class(cls): print('teardown-class') def test01(self): print('class-test01') def test02(self): print('class-test02') def setup_function(): print('setup_function') def teardown_function(): print('teardown_function') def setup_module(): print('setup module') def teardown_module(): print('teardown_module') def test1(): print('测试1') def test2(): print('测试2') if __name__=='__main__': pytest.main(['test04.py','-sv']) pytest allure生成测试报告 第一步安装:pip install allure-pytest 第二步下载:官网https://github.com/allure-framework/allure2/releases 之后要配置环境变量:下载的包可放任意位置,下载allure之后将bin路径配置到环境变量中,重启电脑 重新配置Java home路径(一定要注意编辑路径时看不到分号,保存完毕后分号就出来了,这个时候需要在新的文本框中重新配置路径)
问题:
cmd 下 ,输入:allure 点击【enter】 后 界面无如何显示并自动打开allure.py文件。如下图:
![]()
解决方案: 1、先确定JDK 环境变量的配置是否正确 2、确定无误后,进入到D:\Program Files\allure-2.17.2\bin目录下,打开 PowerShell 界面,输入 allure.bat 发现可以正常输出相应的帮助信息 3、于是,在 cmd 界面中 输入 allure.bat 可以正常显示相应的帮助信息 在使用的过程中,用 allure.bat ;如: 注意:生成测试报告,必须在命令行执行 pytest --alluredir ./reports test02.py allure.bat serve ./reports 启动allure 查看报告(没有问题的就用allure serve ./reports )
完整示例: import pytest import allure @pytest.fixture(scope='session') def login(): print('用例先登录') @allure.step('步骤1:点xxx') def step_1(): print('1111') @allure.step('步骤2:点xxx') def step_2(): print('222') @allure.feature('编辑页面') class TestEditPage(): #编辑页面 @allure.story('这是一个xxx的用例') def test1(self,login): '''先登录,再去执行xxx''' step_1() step_2() print('xxx的用例') @allure.story('打开a的页面') def test2(self,login): '''先登录,再去执行''' print('a的页面') if __name__=='__main__': #注意:生成测试报告,必须在命令行执行 #pytest --alluredir ./reports test02.py #allure.bat serve ./reports 启动allure 查看报告 pytest.main(['--alluredir','./reports','test02.py'])




日志时间: my_format='%(asctime)s-%(filename)s-%(module)s-%(lineno)d' logging.basicConfig( filename='my.log', level=logging.INFO, format=my_format ) logging.info('info') logging.debug('debug') logging.warning('warming') logging.error('error') logging.critical('critical') 最后写在my.log文件里




def get_logger(): logger = logging.getLogger('mylogger') logger.setLevel(logging.DEBUG) all_path=os.path.dirname(os.path.dirname(__file__))+'\\logs\\all.txt' rf_handle = logging.handlers.TimedRotatingFileHandler(all_path, when='midnight', interval=1, backupCount=7,atTime=datetime.time(0, 0, 0, 0)) rf_handle.setFormatter(logging.Formatter('%(asctime)s-%(levelname)s-%(message)s')) err_path=os.path.dirname(os.path.dirname(__file__))+'\\logs\\error.txt' f_handle = logging.FileHandler(err_path,encoding='utf-8') f_handle.setLevel(logging.ERROR) f_handle.setFormatter(logging.Formatter('%(asctime)s-%(levelname)s-%(filename)s[:%(lineno)d]-%(message)s')) logger.addHandler(rf_handle) logger.addHandler(f_handle) return logger DDT参数化
import pytest,csv # import warnings # warnings.filterwarnings("ignore", category=DeprecationWarning) #读取csv文件 def get_data(): with open('test.csv','r') as f: lst=csv.reader(f) mydata=[] for row in lst: #extend() 函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表) mydata.extend(row) return mydata @pytest.mark.parametrize('name',get_data()) def test1(name): print(name) if __name__== '__main__': # get_data() pytest.main(['-sv','test_csv.py'])
import pytest import json def get_data(): with open('data_json.json','r') as f: lst=[] data=json.load(f) lst.extend(data['keys']) return lst @pytest.mark.parametrize('name',get_data()) def test1(name): #遍历 print(name) if __name__ == '__main__': # print(get_data()) pytest.main(['-sv','test_json.py'])
import pytest import xlrd def get_data(): filename='test.xls' wb=xlrd.open_workbook(filename) sheet=wb.sheet_by_index(0) rows=sheet.nrows cols = sheet.ncols lst=[] for row in range(rows): for col in range(cols): cell_data=sheet.cell_value(row,col) lst.append(cell_data) return lst @pytest.mark.parametrize('name',get_data()) def test1(name): print(name) if __name__ == '__main__': pytest.main(['-sv','test_excel.py'])1、安装mysql服务,mysql里建库建表
2、pip install mysqlclient import MySQLdb import pytest conn=MySQLdb.connect( user = 'root', passwd='123456', host='localhost', port=3306, db='test_db' ) def get_data(): query_sql='select id,username,pwd from user_tbl' lst=[] try: cursor=conn.cursor() #获取游标 cursor.execute(query_sql) r=cursor.fetchall() for x in r: u=(x[0],x[1],x[2]) #0、1、2代表的是id,username,pwd lst.append(u) return lst finally: cursor.close() conn.close() @pytest.mark.parametrize('id,name,pwd',get_data()) def test1(id,name,pwd): print(id,name,pwd) if __name__ == '__main__': pytest.main(['-sv','test_db.py'])
#读取yaml文件 1、ymal文件如下:env.yaml - name: 登录成功 request: url: xxx method: get headers: null data: password: 123456 email: 111@11.com validate: assert: null - name: 登录失败 request: url: xxx method: get headers: null data: password: 123456 email: 111@11.com validate: assert: null
python文件读取yaml和调用: import os import yaml,pytest # yaml_file_path=os.getcwd()+r'/data/env.yml' def read_yaml(yaml_file_path): with open(yaml_file_path, encoding="utf-8") as f: value = yaml.load(f, Loader=yaml.FullLoader) # print(value) return value # print(read_yaml('env.yaml')) @pytest.mark.parametrize('caseinfo',read_yaml('env.yaml')) def test_yaml(caseinfo): print(caseinfo) print('用例名称',caseinfo['name']) print('请求url',caseinfo['request']['url'])
![]()
有了pytest后ddt就用的较少了,因为pytest有自己的参数化数据驱动的方式 pip install ddt import os from ddt import ddt,data,unpack,file_data import unittest def get_data(): testdata=[{'name':'tom','age':20},{'name':'kite','age':19}] return testdata @ddt class MyTestCase(unittest.TestCase): #读取元组数据-单组元素 @data(1,2,3) def test1(self,value): print('测试1',value) #读取元组数据,多组元素 @data((1,2,3),(4,5,6)) def test2(self,value): print('测试2',value) #读取元组数据-拆分数据 @data((1,2,3),(4,5,6)) @unpack #拆分数据 pack打包 def test3(self,value1,value2,value3): print('测试3',value1,value2,value3) #列表 @data([{'name':'tom','age':20},{'name':'kite','age':19}]) def test4(self,value): print('测试4',value) #字典 @data({'name':'tom','age':20},{'name':'kite','age':19}) def test5(self,value): print('测试5',value) #字典拆分 @data({'name':'tom','age':20},{'name':'kite','age':19}) def test6(self,name,age): print('测试6',name,age) #变量或者方法调用 testdata=[{'name':'tom','age':20},{'name':'kite','age':19}] #@data(*testdata) #调用testdata变量 @data(get_data()) #调用方法 def test7(self,value): print('测试7',value) #读文件 @file_data(os.getcwd()+'/data_json.json') def test8(self,value2): print('测试8',value2) if __name__ == '__main__': unittest.main()



















1
浙公网安备 33010602011771号