Selenium(7):元素定位_css_selecto定位

Css_selector是什么

  CSS是一个被用来描述如何在屏幕等处渲染HTML和XML文档的语言。CSS使用选择器来为文档中的元素绑定样式属性。

  选择器(selector)是用来在树中匹配元素的模式,选择器对HTML和XML进行了优化,被设计用来在注重性能的代码中执行。Selenium官网的Document里极力推荐使用Css_selector,而不是XPath来定位元素。

Css_selector常用如下6种定位元素的方法(不支持文本定位)

 1、通过绝对路径定位

  绝对路径是从网页的根节点html开始,逐层去查找需要定位的元素。

  此方法缺点显而易见,当页面元素位置发生改变时,都需要修改,因此,并不推荐使用。

代码如下:

以百度首页搜索框为例

import os
from selenium import webdriver
from selenium.webdriver.common.by import By

current_path = os.path.dirname(os.path.abspath(__file__))  # 当前路径
driver_path = os.path.join(current_path,'../webdriver/chromedriver.exe')  # driver路径
driver = webdriver.Chrome(executable_path=driver_path)  # Firefox,Ie等

driver.get('https://www.baidu.com/')  # 打开网站
# 绝对路径定位
# 以html body为开头,使用空格分开层次结构 遇到id使用# 遇到class使用.
driver.find_element(By.CSS_SELECTOR,'html body div#wrapper.wrapper_new div#head div#head_wrapper.head_wrapper.s-isindex-wrap.nologin div.s_form.s_form_nologin div.s_form_wrapper.soutu-env-nomac.soutu-env-index form#form.fm span.bg.s_ipt_wr.new-pm
import logging from core.keywords import Keywords from utils.Keywords_utils import kw_step class AssertKeywords(Keywords): @kw_step def assert_url(self, step): """URL断言""" expected_url = step['data'] actual_url = self.driver.current_url # assert的参数2, 断言失败时,抛出异常 AssertException: 参数2信息, 如果没有捕获异常, 会导致后续步骤不执行 assert expected_url in actual_url, f"❌当前URL: {actual_url} 不包含 预期URL: {expected_url}" logging.info(f"✅当前URL: {actual_url} 包含 预期URL: {expected_url}") # 这样可以捕获异常, 不影响后续步骤的执行 # try: # assert expected_url in actual_url # logging.info(f"✅当前URL: {actual_url} 包含 预期URL: {expected_url}") # except AssertionError: # logging.error(f"❌当前URL: {actual_url} 不包含 预期URL: {expected_url}") @kw_step def assert_title(self, step): """title断言""" expected_title = step['data'] actual_title = self.driver.title assert expected_title in actual_title, f"❌当前title: {actual_title} 不包含 预期title: {expected_title}" logging.info(f"✅当前title: {actual_title} 包含 预期title: {expected_title}") @kw_step def assert_text(self, step): """text断言""" expected_text = step['data'] actual_text = self.find(step).text if expected_text is None or expected_text == "": raise ValueError("❌断言失败:Excel中断言预期内容为空,请填写断言文本") assert expected_text in actual_text, f"❌当前text: {actual_text} 不包含 预期text: {expected_text}" logging.info(f"✅当前text: {actual_text} 包含 预期text: {expected_text}") @kw_step def assert_alert_text(self, step): """alert文本断言""" alert = self.driver.switch_to.alert expected_text = step['data'] actual_text = alert.text assert expected_text in actual_text, f"❌当前text: {actual_text} 不包含 预期text: {expected_text}" logging.info(f"✅当前text: {actual_text} 包含 预期text: {expected_text}") @kw_step def assert_element_exists(self, step): """元素存在断言""" element = self.find(step) assert element, f"❌元素不存在: {element}" logging.info(f"✅元素存在: {element}")import logging import os from tkinter.tix import Select import select import time import allure from selenium.common import TimeoutException from selenium.webdriver import ActionChains, Keys from selenium.webdriver.ie.webdriver import WebDriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from utils.Keywords_utils import kw_step class Keywords: def __init__(self, driver): self.driver = driver # 基础操作 # 基础操作 def find(self, step): """查找元素""" wait = WebDriverWait(self.driver, 10) locator = step['by'], step['value'] try: # 如果索引为 None 则定位单个元素, 反之则定位一组元素 if step["index"] is None: return wait.until(EC.presence_of_element_located(locator)) else: return wait.until(EC.presence_of_all_elements_located(locator))[step['index']] except TimeoutException: logging.info(f"❌元素定位失败, 元素定位信息为: {locator}") @kw_step def open(self, step): self.driver.get(step['data']) @kw_step def input(self, step): self.find(step).send_keys(step['data']) @kw_step def click(self,step): self.find( step).click() @kw_step def wait(self, step): time.sleep(step['data']) @kw_step def shot(self, step): '''截图''' png = self.driver.get_screenshot_as_png() now_time = time.strftime("%Y-%m-%d %H_%M_%S") allure.attach( png, f'第{step["step_num"]}步_{now_time}', allure.attachment_type.PNG ) @kw_step def refresh(self,step): self.driver.refresh() @kw_step def forward(self,step): self.driver.forward() @kw_step def back(self,step): self.driver.back() @kw_step def switch_to_window(self,step): handles = self.driver.window_handles self.driver.switch_to.window(handles[step['data']]) # 下拉框选择,通过文本选择,通过值选择 @kw_step def select_by_text(self,step): # 处理原生select,通过文本选择 select = Select(self.find(step)) select.select(self.find(step), step['data']) @kw_step def select_by_value(self,step): # 处理原生select,通过值选择 select = Select(self.find(step)) select.select_by_value(step['data']) @kw_step def accept_alert(self,step): self.driver.switch_to.alert.accept() @kw_step def dismiss_alert(self,step): self.driver.switch_to.alert.dismiss() @kw_step def scroll(self,step): ''' 滚动到某个绝对位置坐标 step['data'],数据要写成{'x':100,'y':100} 通过eval函数将"{'x':100,'y':100}"转换为字典 ''' position = eval(step['data']) js = f"window.scrollTo({position['x']},{position['y']})" self.driver.execute_script(js) def js_execute(self,step): ''' 执行js代码 step['data'],数据要写成{'x':100,'y':100} 通过eval函数将"{'x':100,'y':100}"转换为字典 ''' js = step['data'] self.driver.execute_script(step["data"]) @kw_step def double_click(self,step): ''' 双击某个元素 ''' action = ActionChains(self.driver) action.double_click(self.find(step)) action.perform() @kw_step def right_click(self,step): ''' 右击某个元素 ''' action = ActionChains(self.driver) action.context_click(self.find(step)) action.perform() @kw_step def hover(self,step): ''' 鼠标悬停某个元素 ''' element = self.find(step) action = ActionChains(self.driver) action.move_to_element(element) action.perform() @kw_step def drag_and_drop(self, step): """拖拽""" # step['data'] 数据要写成 {"by": "xpath", "value": "xxx"} 的形式 source = self.find(step) # 目标元素数据需要处理 target_dict = eval(step["data"]) target = self.driver.find_element(target_dict["by"], target_dict["value"]) action = ActionChains(self.driver) action.drag_and_drop(source, target).perform() @kw_step def enter(self, step): # 回车 element = self.find(step) element.send_keys(Keys.ENTER) @kw_step def upload(self, step): """上传文件""" # 相对路径转化为绝对路径 relative_path = step['data'] absolute_path = os.path.abspath(relative_path) element = self.find(step) element.send_keys(absolute_path) # frame操作: 切换到某个frame, 切回主文档 @kw_step def switch_to_frame(self, step): """根据frame元素把焦点切换到某个frame""" element = self.find(step) self.driver.switch_to.frame(element) @kw_step def switch_to_default_content(self, step): """从frame切回到主文档""" self.driver.switch_to.default_content() # utils/allure_utils.py import allure def allure_init(case): """ 初始化allure报告信息,设置测试用例的基本属性 Args: case (dict): 测试用例数据字典 """ # 设置模块(feature) if "feature" in case: allure.dynamic.feature(case["feature"]) # 设置场景(story) if "story" in case: allure.dynamic.story(case["story"]) else: allure.dynamic.story("默认场景") # 设置标题 if "title" in case: allure.dynamic.title(case["title"]) else: allure.dynamic.title(f"用例ID: {case.get('id', '未知ID')}") # 设置用例ID作为描述 if "id" in case: allure.dynamic.description(f"用例ID: {case['id']}") # 添加测试用例参数信息 test_params = {} if "id" in case: test_params["用例ID"] = case["id"] if "feature" in case: test_params["模块"] = case["feature"] if "story" in case: test_params["场景"] = case["story"] if "title" in case: test_params["标题"] = case["title"] if test_params: allure.dynamic.parameter("测试信息", test_params)import allure def allure_init(case): allure.dynamic.feature(case["feature"]) allure.dynamic.story(case["story"]) # allure.dynamic.title(case["title"]) allure.dynamic.title(f'ID:{case["id"]} -- {case["title"]}')import logging from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.edge.service import Service as EdgeService from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.microsoft import EdgeChromiumDriverManager from config.config import BROWSER_TYPE, HEADLESS, DRIVER_TYPE, CHROME_DRIVER_PATH, Edge_DRIVER_PATH def get_driver(): driver = None if BROWSER_TYPE == 'chrome': driver = get_chrome_driver() if BROWSER_TYPE == 'edge': driver = get_edge_driver() return driver def get_chrome_driver(): options = webdriver.ChromeOptions() options.add_argument("--start-maximized") options.add_argument("--headless=new") if HEADLESS else None if DRIVER_TYPE == "local": service = Service(CHROME_DRIVER_PATH) else: service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service, options=options) logging.info("启动Chrome浏览器成功") return driver def get_edge_driver(): options = webdriver.EdgeOptions() options.add_argument("--start-maximized") options.add_argument("--headless=new") if HEADLESS else None if DRIVER_TYPE == "local": service = EdgeService(Edge_DRIVER_PATH) else: service = EdgeService(EdgeChromiumDriverManager().install()) driver = webdriver.Edge(service=service, options=options) logging.info("启动Edge浏览器成功") return driverimport logging import allure def kw_step(func): ''' 装饰器,用于记录allure步骤和日志信息 ''' def wrapper(self,step): with allure.step(f'第{step["step_num"]}步,{step["step_name"]}'): # 记录日志 logging.info(f'第{step["step_num"]}步,{step["step_name"]}') # 返回原函数 return func(self,step) return wrapper import openpyxl def read_excel(): workbook = openpyxl.load_workbook("../data/ui_test_run.xlsx") data = [] # 先把所有用例数据读取出来,再进行筛选,合格的用例才添加到data中 all_cases = [] # 定义当前用例数据,组织处理每一条用例数据的问题 current_case = None # worksheet = workbook["Sheet1"] for worksheet in workbook.worksheets: keys = [cell.value for cell in worksheet[2]] for row in worksheet.iter_rows(min_row=3,values_only=True): dict_data = dict(zip(keys,row)) # 只要id不为none,代表这是一条新的用例 if dict_data['id'] is not None: # 组织用例的过程 current_case = { "id": dict_data["id"], "feature": dict_data["feature"], "story": dict_data["story"], "title": dict_data["title"], "steps" : [{ "step_num" : dict_data["step_num"], "step_name": dict_data["step_name"], "keyword" : dict_data["keyword"], "by": dict_data["by"], "value": dict_data["value"], "data": dict_data["data"], "index": dict_data["index"] }], "is_true" : dict_data["is_true"] } # 临时存起来 all_cases.append(current_case) pass # id为none的情况下,就是用例里面的步骤(提取步骤) elif current_case is not None: current_case["steps"].append({ "step_num" : dict_data["step_num"], "step_name" : dict_data["step_name"], "keyword": dict_data["keyword"], "by": dict_data["by"], "value": dict_data["value"], "data": dict_data["data"], "index": dict_data["index"] }) # 过滤用例数据,只保留is_true为True的值 data = [case for case in all_cases if case["is_true"]] workbook.close() return data import pytest from selenium import webdriver from selenium.webdriver.ie.service import Service @pytest.fixture(scope='function',autouse=True) def driver_handler(): # 创建浏览器对象 driver = webdriver.Chrome(service=Service(r"D:\Python\stu_practice\driver\chromedriver.exe")) # yield两个作用:1、暂停并恢复执行 2、返回值但是不终止函数 # 如果想要把返回值传递到测试函数中,最简单的方法就是测试函数显示调用这个夹具 yield driver # (手动)关闭浏览器 driver.quit()import logging from selenium import webdriver from selenium.webdriver.chrome.service import Service import pytest import allure from core.assert_Keywords import AssertKeywords from core.keywords import Keywords from utils.allure_utils import allure_init from utils.read_excel import read_excel class TestRunner: # 读测试用例文件中的全部数据, 用属性保存即可 data = read_excel() # 不确定后续是否需要提取(暂时注释) # all = {} @pytest.mark.parametrize("case", data) def test_case(self, case, driver_handler): # all = self.all # case = eval(Template(str(case)).render(all)) # 初始化allure报告 allure_init(case) # 测试用例的描述信息日志 logging.info(f'用例ID:{case["id"]} 模块:{case["feature"]} 场景:{case["story"]} 标题:{case["title"]}') # 创建关键字对象 keywords = Keywords(driver_handler) assert_keywords = AssertKeywords(driver_handler) # 执行每一步 for step in case["steps"]: # 遍历查找关键字 for i in [keywords, assert_keywords]: # 使用hasattr判断关键字是否存在 if hasattr(i, step["keyword"]): func_name = i.__getattribute__(step["keyword"]) func_name(step) break # for...else...语法: 如果没有遇到break则执行else中的代码, 如果遇到break则跳过else else: raise AttributeError(f'❌未找到关键字:{step["keyword"]}')import os import pytest if __name__ == "__main__": pytest.main(["-vs", "./testcases/test_runner.py", "--alluredir", "./report/json_report", "--clean-alluredir"]) os.system("allure generate ./report/json_report -o ./report/html_report --clean") 先熟悉下我的ui自动化代码框架
最新发布
09-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值