背景前端开发公司要求项目写自动化测试没测试经验过了两遍Python语法开始了自动化测试的学习与使用你是否正在寻找一种从零开始的方法来为你的项目编写 UI 自动化测试如果是这样你来对地方了本文将利用我的学习和实际经验向你介绍 Playwright 和 Python 在 UI 自动化测试中的应用这不仅是一个简单的教程更是一个结合了真实业务逻辑片段的实用入门指南引言在较长的产品周期中自动化测试是确保应用质量和性能的关键步骤。这里记录了一个前端开发者从零开始学习Python和Playwright来构建自动化测试的过程。Playwright 是一个强大的自动化测试工具支持多种浏览器本文将从基础安装到脚本编写的每一个步骤进行详细介绍重点是示例。建议等你遇到问题再来查或许会帮助你解决测试大佬勿喷欢迎指正playWright 自带的元素获取工具效果展示工具源文档playwright codegen 是 Playwright 测试框架的一个功能它可以自动记录你在浏览器中的操作并生成相应的测试代码。这是一个非常有用的工具尤其是对于快速开始编写测试或者为现有的用户操作创建自动化测试脚本。使用 playwright codegen 的步骤如下安装 Playwright如果你还没有安装 Playwright可以通过下面命令安装pip install playwright运行 Codegen在命令行中输入 playwright codegen 后跟你想要测试的网站的 URL。例如playwright codegen https://www.baidu.com如果不提供 URLcodegen 会打开一个空白的浏览器窗口您可以在其中输入您想要测试的网站的 URL。记录操作 在打开的浏览器窗口中执行你想要测试的操作。Playwright 会监视您的操作并生成相应的测试代码。生成断言 你可以通过点击 Playwright Inspector 窗口中的图标来生成断言然后点击页面上的元素来对其进行断言。停止记录 完成操作后点击“停止记录”按钮。然后你可以使用“复制”按钮将生成的代码复制到您的编辑器 中。编辑和保存测试 你可以在编辑器中进一步编辑和完善生成的测试代码然后将其保存为测试文件。 playwright codegen 是快速生成测试代码的强大工具它可以节省时间并确保测试准确录制了用户的实际操作。如下元素获取示例语法page.locator()page.locator(.menu-delete-modal)是 Playwright 测试库中的一个语法用于定位页面上的元素并且可以链式调用多种操作。这里.menu-delete-modal是一个CSS类它会选择页面上所有具有menu-delete-modal类的元素。page.locator方法返回一个Locator对象代表了页面上的一个或多个DOM元素可以对这些元素执行各种操作比如点击、填写表单等。以下是一些 Python 示例展示了获取元素的常用方法page.locator()定位元素使用 CSS 选择器或 XPath 来定位页面上的元素。page.locator(.example).click() # 点击类名为example的元素文本内容定位通过元素的文本内容来定位。page.locator(text登录).click() # 点击文本为“登录”的元素属性定位通过元素的属性来定位。page.locator([nameemail]).fill(exampleexample.com) # 填写name属性为email的输入框等待元素等待元素出现在页面上。page.locator(.loading).wait_for(statehidden) # 等待加载元素消失默认等待元素出现获取元素属性获取元素的属性值。href page.locator(.link).get_attribute(href) # 获取链接的href属性处理多个元素对页面上的一组元素进行操作。items page.locator(.list-item).element_handles() for item in items: item.click() # 点击每个列表项元素的父级元素: 拿到父级获取父级下其他数据是。items page.locator(.list-item).element_handles() for item in items: item.click() # 点击每个列表项示例- 获取类名 menu-delete-modal 弹框下一个按钮parent_element page.locator(text登录).locator(xpath..)示例- 使用locator选择器来点击一个确定按钮page.locator(.menu-delete-modal).click()注意在严格模式返回的是一个或多个元素存在差异。如果是一个元素可以直接执行Locator对象上的元素操作方法,一些静态方法如click(), inner_html()...多个元素的情况不能执行click(),inner_html()...通过count() 可以查看有多少元素。如图一个元素多个元素想要点击其中一个元素可以使用.nth(index),其中index是从 0 开始的索引page.locator(.el-button).nth(0).click() # 点击第一个按钮 page.locator(.el-button).nth(1).click() # 点击第二个按钮 # 以此类推...如果需要对所有匹配的元素执行操作可以遍历它们# 方式1 buttons page.locator(.el-button) count buttons.count() for i in range(count): buttons.nth(i).click() # 方式2 button_handles page.locator(.el-button).element_handles() # 遍历并点击每个按钮 for button_handle in button_handles: button_handle.click() page.get_by_role()get_by_role是 Playwright 测试库中的一个功能它允许你根据元素的 ARIA 角色 来定位页面上的元素。当您使用get_by_role时通常也会传递一个可访问名称以便精确地定位到特定元素。例如如果你想定位并且点击页面上名为“登录”的按钮可以使用以下代码page.locator(.menu-delete-modal).get_by_role(button, name确定).click()这里按钮在浏览器的元素界面是button下包裹 span确定文字在span内如下name的值采用的是模糊匹配name为确也可以正常执行对前端开发者来说直接使用 HTML 元素和类名选择器更直观和方便。ARIA 角色主要是为了提高网页的无障碍性帮助使用辅助技术的用户更好地理解和导航网页内容。示例- get_by_text 精准获取一个元素界面代码如下page.locator(.menu-config-dropdown-menu).get_by_text(员工)执行发现会获取两个元素{Error}strict mode violation: locator(.menu-config-dropdown-menu).get_by_text(员工) resolved to 2 elements:…aka get_by_text(员工, exactTrue)…aka get_by_text(全部员工)想要精准匹配如下哦page.locator(.menu-config-dropdown-menu).get_by_text(员工, exactTrue)示例- 获取元素的父级元素期望的效果是通过标题名称获取到包装元素最终调用如下menu_form_design_page.get_component_by_comp_name(单行输入333).click()场景如下函数如下def get_component_by_comp_name(self, comp_name: str) - ComponentWrapper: # 通过组件名获取组件返回一个组件 component self.form_item_list.get_by_text(f{comp_name}, exactTrue).first # 通过名称匹配到标题 if component.count() ! 1: raise Exception(f未找到组件{comp_name}) while component.get_attribute(data-ui-test-component-type) is None: # 循环向上找目标父元素 component component.locator(xpath..) return ComponentWrapper(self.page, component) # 自定的一些目标元素包装类用于封装一些常用属性和方法ComponentWrapper类如下from playwright.async_api import Locator, Page class ComponentWrapper: def __init__(self, page: Page, component_locator: Locator): self.page component_locator.page self.comp_locator component_locator self.comp_type component_locator.get_attribute(data-ui-test-component-type) self.comp_name component_locator.get_attribute(data-ui-test-component-label) self.copy_btn component_locator.locator(.svg-x-lib-copy-add).first self.delete_btn component_locator.locator(.svg-delete-icon).first self.delete_dialog page.locator(.delete-component-modal).first self.cancel_btn self.delete_dialog.get_by_role(button, name取消) self.confirm_btn self.delete_dialog.get_by_role(button, name确认) self.comp_locator.click self.component_wrapper_click # 重写click方法 # 删除组件 def delete_comp(self): self.comp_locator.click() self.delete_btn.click() if self.delete_dialog.is_visible(): self.confirm_btn.click() # 点击方法 def component_wrapper_click(self): self.comp_locator.evaluate(element {element.click()}) # 可以执行JavaScript代码前端觉得很合理元素操作示例- 拖拽元素# 向指定子表组件中添加指定类型组件传入指定子表名称和组件类型列表 def add_component_to_specified_son_table(self, son_table_name: str, component_enum_list: list[OriginComp], callback_fn: callable None): # 根据名称获目标表格组件 son_table self.get_component_by_comp_name(son_table_name) # 悬停在目标配置区域滚动鼠标到子表组件的位置之前发现的一个问题如果子表格组件不在可视区域内会导致无法拖拽某些组件会有问题 self.form_item_list.hover() self.page.mouse.wheel( delta_yson_table.comp_locator.evaluate((el) el.getBoundingClientRect())[y], delta_x0 ) # 断言子表格组件的数量为1如果不为1说明定位到的不是唯一的子表格组件 assert son_table.comp_locator.count() 1 # 获取原始组件列表被拖拽内容 component_type_list Components.comp_enum_list_to_type_list(component_enum_list) component_list self.get_origin_components(component_type_list) for component in component_list: component.hover() self.page.wait_for_timeout(500) self.page.mouse.down() # 获取子表格组件的矩形区域 form_panel_form_widget_son_table_rect son_table.comp_locator.locator( .form-widget-son-table .grid-draggable .drag-area ).evaluate((el) el.getBoundingClientRect()) # 计算矩形区域的x和y坐标相对于页面左上角 form_panel_form_widget_son_table_rect_x form_panel_form_widget_son_table_rect[x] 50 form_panel_form_widget_son_table_rect_y form_panel_form_widget_son_table_rect[y] 80 # 移动鼠标到计算出的y坐标位置分两步也是因为有些组件从左上角开始拖拽会有问题所以先向下再向右 self.page.mouse.move( x0, yform_panel_form_widget_son_table_rect_y, steps10 ) self.page.mouse.move( xform_panel_form_widget_son_table_rect_x, yform_panel_form_widget_son_table_rect_y, steps10 ) self.page.wait_for_timeout(500) self.page.mouse.up() # 完成一次拖拽操作之后执行回调函数。我在Python里面写JavaScript代码前端觉得很合理 if callback_fn: callback_fn()示例- Python playWright 执行浏览器JavaScript脚本代码点击元素def click_element(self, element: Locator): # 点击元素 element.evaluate(element {element.click()})有的时候playWright的click()方法会有问题点击不到目标元素可以使用JavaScript代码来执行点击操作下面是一个组件的封装类里面覆盖了点击元素的方法使用了evaluate方法执行JavaScript代码from playwright.async_api import Locator, Page class ElementManager: def __init__(self, page: Page, element_locator: Locator): self.page element_locator.page self.elem_locator element_locator self.elem_type element_locator.get_attribute(data-test-elem-type) self.elem_label element_locator.get_attribute(data-test-elem-name) self.copy_element element_locator.locator(.icon-copy-element).first self.remove_element element_locator.locator(.icon-remove).first # 重写点击事件 self.elem_locator.click self.element_click_action # 元素点击动作 def element_click_action(self): self.elem_locator.evaluate(element {element.click()})最后下方这份完整的软件测试 视频教程已经整理上传完成需要的朋友们可以自行领取【保证100%免费】​​​软件测试面试文档我们学习必然是为了找到高薪的工作下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料并且有字节大佬给出了权威的解答刷完这一套面试资料相信大家都能找到满意的工作。