Selenium 自动化测试最佳实践

news2024/9/20 8:05:09

1 编码前的准备工作与基本指导思想

测试一个网站就是针对该网站测试场景的一次项目开发,所以项目开发中的理念与思想可以借鉴过来。接到测试需求后,不要一开始就陷入按钮、字段、下拉框等页面元素怎么操作的技术细节当中,而要站在最终用户的角度去分析这个测试需求的交互逻辑和依赖关系,从而将其拆解为一个个相对独立的测试用例。而对于每一个测试用例,并不是每一步都必须使用 Selenium 去自动化实现,而是要根据实际情况将关键的部分自动化,其它非关键的部分可以通过植入数据或调用 API 来实现。

拆解完成后,应该如何编排测试代码?下面将探讨这个问题。

2 如何编排测试代码?

如何编排测试代码?即采用何种策略组织与编排测试代码,从而让代码更简洁且更好维护。

我们依然使用实际的例子来说明将要探讨的问题。

下面是一个「Selenium Web 表单示例页面」的自动化测试动图:

Selenium Web 表单示例页面

可以看到,该动图展示的自动化测试代码对表单页面进行了文本输入、密码输入、下拉框选项选择和日期输入,并点击了提交按钮,最后跳转至已提交页面。

对应动图原始的 Python 测试代码(original_form_test.py)如下:

 

python

代码解读

复制代码

from unittest import TestCase from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.select import Select from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestForm(TestCase): def setUp(self) -> None: self.driver = webdriver.Chrome() self.addCleanup(self.driver.quit) def test_web_form(self) -> None: # 打开表单页面 self.driver.get('https://www.selenium.dev/selenium/web/web-form.html') self.assertEqual(self.driver.title, 'Web form') # Text 输入 text_input_elem = self.driver.find_element(By.ID, 'my-text-id') text_input_elem.send_keys('Selenium') # Password 输入 password_elem = self.driver.find_element(By.NAME, 'my-password') password_elem.send_keys('Selenium') # Dropdown 选择 dropdown_elem = Select(self.driver.find_element(By.NAME, 'my-select')) dropdown_elem.select_by_value('2') # 日期输入 date_input_elem = self.driver.find_element(By.XPATH, '//input[@name="my-date"]') date_input_elem.send_keys('05/10/2023') # 点击 Submit 按钮 submit_button_elem = self.driver.find_element(By.XPATH, '//button[@type="submit"]') submit_button_elem.click() # 等待进入已提交页面 WebDriverWait(self.driver, 10).until(EC.title_is('Web form - target page')) # 断言 message = self.driver.find_element(By.ID, 'message').text self.assertEqual(message, 'Received!')

可以看到,这是一种常见的最直接的测试代码编写方式。

然而这种写法存在几个问题:

  • 测试代码(assertEqual)和定位与操作元素的代码(find_element ... send_keys ... click)耦合在一起。这样,如果元素定位标识发生变化或者元素操作方式发生变化,这块测试代码都需要修改。

  • 如果要编写针对该页面本身或者依赖该页面的其它测试代码,定位与操作元素的代码都需要重写一遍。

要解决如上问题,须从代码结构与代码编排上着手,即引入一种设计模式 —— 页面对象模型。

页面对象模型(Page Object Model)借鉴了面向对象编程的思想,是一种在自动化测试中被广泛使用的设计模式,用于减少重复代码并增加代码的可维护性。页面对象是一个面向对象的类,其将同一页面的 Web 元素存储在同一个对象中;当需要与该对象的 UI 进行交互时,不直接访问该页面的 Web 元素,而通过调用该对象提供的方法来实现。这样做的好处是如果某个页面的 UI 发生了改变,测试代码无须更改,只需要更改对应页面对象内的代码即可。

总结一下,使用页面对象模型的好处包括:

  • 测试代码与特定于页面的代码分离(增加了简洁性);
  • 页面元素和功能被封装在页面对象的属性和方法中,而不是让其分散在整个测试代码中(减少了重复代码并增加了可维护性)。

下面就使用页面对象模型设计一下测试项目的目录结构:

 

shell

代码解读

复制代码

$ tree . ├─ pages │ ├─ form.py │ └─ form_target.py └─ optimized_form_test.py

可以看到,针对各个页面的页面对象被放在pages目录下,编写测试用例时调用其对应的方法即可。

下面看一下优化后的代码。

Form页面对象代码(form.py):

 

python

代码解读

复制代码

from selenium.webdriver.common.by import By from selenium.webdriver.support.select import Select from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from pages.form_target import FormTarget from typing import Self class Form: def __init__(self, driver) -> None: self.driver = driver def open(self) -> Self: self.driver.get('https://www.selenium.dev/selenium/web/web-form.html') return self def get_title(self) -> str: return self.driver.title def input_text(self, text: str) -> Self: elem = self.driver.find_element(By.ID, 'my-text-id') elem.send_keys(text) return self def input_password(self, password: str) -> Self: elem = self.driver.find_element(By.NAME, 'my-password') elem.send_keys(password) return self def select_from_dropdown(self, value: str) -> Self: elem = Select(self.driver.find_element(By.NAME, 'my-select')) elem.select_by_value(value) return self def input_date(self, date: str) -> Self: elem = self.driver.find_element(By.XPATH, '//input[@name="my-date"]') elem.send_keys(date) return self def submit(self) -> FormTarget: elem = self.driver.find_element(By.XPATH, '//button[@type="submit"]') elem.click() # 等待进入已提交页面 WebDriverWait(self.driver, 10).until(EC.title_is('Web form - target page')) # 返回 FormTarget 对象 return FormTarget(self.driver)

FormTarget页面对象代码(form_target.py):

 

python

代码解读

复制代码

from selenium.webdriver.common.by import By class FormTarget: def __init__(self, driver) -> None: self.driver = driver def get_message_text(self) -> str: return self.driver.find_element(By.ID, 'message').text

使用如上两个页面对象后的测试代码(optimized_form_test.py):

 

python

代码解读

复制代码

from unittest import TestCase from selenium import webdriver from pages.form import Form class TestForm(TestCase): def setUp(self) -> None: self.driver = webdriver.Chrome() self.addCleanup(self.driver.quit) def test_web_form(self) -> None: # 打开表单页面 form_page = Form(self.driver) form_page.open() self.assertEqual(form_page.get_title(), 'Web form') # 输入 form_target_page = form_page.input_text('Selenium') \ .input_password('Selenium') \ .select_from_dropdown('2') \ .input_date('05/10/2023') \ .submit() # 断言 message = form_target_page.get_message_text() self.assertEqual(message, 'Received!')

可以看到,经过优化后的代码清晰了许多。

下面总结一下使用页面对象模型时的几个注意事项:

  • 断言是测试逻辑的一部分,应放在测试代码中,因此,页面对象中不应有断言或验证相关的代码;
  • 页面对象只应将页面提供的服务通过公共方法暴露出来,其它内部细节不要暴露出来。

接下来关注一下实现细节,定位器的使用是编写 Selenium 测试代码时大量涉及的工作。下面看一下定位器使用相关的最佳实践。

3 如何根据情况使用适当的定位器?

前文「Selenium WebDriver 基础使用」中介绍了 Selenium 有 8 种基本的元素定位方法。而什么时候使用什么样的定位器呢?下面给出其最佳实践。

id 定位器为首选定位方法,准确快速;若元素没有id,则使用css 选择器;此两种不可用,再选择xpath 定位器(相对前两种性能较差);一般在页面上会有多个相同 tag 的元素,所以tag 定位器一般用于选择一组元素。

综上,本文介绍了构建一个大型测试项目时分析需求的基本指导思想、编排测试代码的实践策略以及使用定位器的推荐顺序。本文涉及的所有代码均已上传至本人 GitHub,欢迎关注。期待阅读完本文,我们对 Selenium 自动化测试从需求分析、编码实现到实现细节上都有了一个可以参考的规范。

  这是我整理的《2024最新Python自动化测试全套教程》,以及配套的接口文档/项目实战【网盘资源】,需要的朋友可以下方视频的置顶评论获取。肯定会给你带来帮助和方向。

【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《MySQL数据库》 可视化工具的使用—/—<3>

一、如何使用可视化工具navicat 1、点击左上角的连接中的MySQL 输入主机地址连接虚拟机,找到自己虚拟机中的ip地址输入即可,连接名随意修改 然后点击测试连接,连接成功即可点击确定 2、新建库 直接鼠标右击连接名称ahao001,点击…

react学习笔记:7

预览:(fetch发送请求、SPA、连续解构赋值、消息订阅、react router路由第三方库) 1、连续解构赋值 总结: 1、连续解构赋值的写法:对象包对象,第二个解构的value一定也是在{}内部的写法 2、消息订阅发布 …

SwiftUI 中 TabView 视图导航栏上按钮丢失问题的解决

问题现象 在某些情况下,SwiftUI 中 TabView 子视图中导航栏上的 ToolbarItem 会消失不见。 如上图所示:在子视图的 Kick Off 导航栏按钮被按下并回退到 TabView 中的主视图之后,其右上角的按钮竟然“神奇”的消失了!该如何解决它呢? 在本篇博文中,您将学到以下内容 问题…

【二分查找】3143. 正方形中的最多点数

本文涉及的基础知识点 C二分查找 LeetCode3143. 正方形中的最多点数 给你一个二维数组 points 和一个字符串 s ,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的 标签 。 如果一个正方形的中心在 (0, 0) ,所有边都平行于坐标轴&…

大数据-Big Data

GPT-4o (OpenAI) 大数据(Big Data)指的是无法使用传统方法和工具在合理的时间内处理和分析的大规模数据集。大数据通常具有以下几种特征,也称为5V特征: 1. Volume(数据量):大数据涉及到大量的信…

深度学习常用语句for param in params问题:为什么修改param之后,params对应元素也随之改变?

def sgd(params, lr, batch_size): #save"""小批量随机梯度下降"""with torch.no_grad():for param in params:param - lr * param.grad / batch_sizeparam.grad.zero_()sgd([w, b], lr, batch_size) 上述代码中,param遍历params的…

深度学习--------------Kaggle房价预测

目录 下载和缓存数据集访问和读取数据集总代码 数据预处理训练K折交叉验证模型选择总代码提交你的Kaggle预测提交Kaggle 下载和缓存数据集 import hashlib import os import tarfile import zipfile import requests# download传递的参数分别是数据集的名称、缓存文件夹的路径…

LabVIEW液压传动系统

开发了一种高效的液压传动系统,其特点在于采用LabVIEW软件与先进的硬件配合,实现能量的有效回收。此系统主要应用于工业机械中,如工程机械和船机械等,通过优化液压泵和马达的测试台设计,显著提高系统的能效和操作性能。…

华为OD机试 - 最长子字符串的长度(二) (Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(D卷C卷A卷B卷)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华…

为什么要推荐R语言?欢迎订阅专栏《R 探索临床数据科学》

统计分析的强大支持: R语言最初是为统计分析而设计的,至今仍然在这方面保持领先地位。无论是基础统计、回归分析、时间序列分析还是高级统计建模,R都能提供丰富的函数和包,帮助我们轻松实现各种统计分析,很简单的代码就…

搭建个人博客需要做哪些事

文章目录 前言搭建步骤站点服务器站点域名注册域名ICP 备案公安备案域名解析 博客图床图床是什么图床搭建 博客站点搭建建站工具本地搭建博客部署 站点运营百度收录百度统计 总结 前言 花了几天时间,搭建了一个个人博客,也算是完成了年初立的一个flag&a…

VSCODE调试程序

1、打开 2、具体测试过程 (1)把路径改成真正执行的程序的绝对路径(${workspaceFolder}这个代表就是项目根目录) (2)然后先注释preLauchTask。 (3)重新编译一下文件,make…

全新神经网络:Kolmogorov-Arnold网络更具解释性,有望为物理学家提供新假设

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

FDE Solver 的 enabled 选项是不开放的

FDE Solver 的 enabled 选项是不开放的 正文正文 在 Mode 工程文件中,只能添加一个 FDE Solver,并且,不同于结构组件,对于结构组件,我们通常可以使用如下脚本将其设置为不启用状态。 比如,我们这里有一个三角型结构。 我们通过如下脚本设置其为不启用状态后, CAD 显示…

准确度与精密度:差异和示例

准确度与精密度:差异和示例 文章目录 一、说明二、准确性的定义三、精度的定义四、飞镖板上的准确度与精确度五、如何记住准确度与精确度六、如何测试准确度和精密度 一、说明 当您依赖数据得出结论时,准确度和精确度是测量的关键属性。这两个概念都适…

Git合并多笔提交为一笔

Git合并多笔提交为一笔 1. 背景 在实际项目开发中,我们会基于生产分支拉出很多需求分支,在需求分支开发完成后再将代码合到生产分支,但随着提交次数越来越多,我们在合到生产分支的时候就得一笔一笔的入库,特别麻烦&a…

day14-测试自动化之Selenium的元素操作、浏览器操作等

一、元素操作 1.1.为什么要学习操作元素的方法? 1).需要让脚本模拟用户给指定元素输入值 2).需要让脚本模拟人为删除元素的内容 3).需要让脚本模拟点击操作 1.2.元素常用操作方法 1).click()点击方法 2).send_keys(value) 输入方法 3).clear(…

手表运动报告生成以及手机展示

一.运动报告组成部分 一般一份运动健康的报告包括以下信息: 1.运动轨迹区。2.报告数据区。(运动总体概览,如距离,时长,训练表现等)3.曲线图表区。(心率曲线,海拔曲线,速度,配速曲线) 二.组成部…

PHP + Laravel + RabbitMQ + Redis 实现消息队列 (三) 消费队列在RabbitMQ和redis中的发布和订阅

发布订阅(Pub/Sub) 对于消息队列传统的模式来说,一个消费者消费一条消息,这条消息被消费之后就不会再次被其它的消费者消费。但是在发布订阅模式中,一条消息是可以被多个消费者消费的,这些消费者其实相当于…

SOMEIP_ETS_021:echoINT8

测试目的: 验证DUT在发送和接收INT8参数时,是否能够保持参数的值和顺序不变。 描述 本测试用例旨在检验DUT在处理包含INT8类型参数的SOME/IP消息时,是否能够正确地发送和接收这些参数,并且确保返回的方法响应消息中的INT8参数值…