目的:总结基于POM设计方式最基础的自动化测试框架,只介绍使用到的技术
1. POM
1.1 POM 的基本概念
- 页面对象:每个页面或页面的一部分都有一个对应的类,这个类封装了页面的元素和操作方法。
- 测试用例:测试用例使用页面对象来执行操作和验证结果。
1.2 POM 的优势
- 可维护性:页面对象封装了页面的元素定位和操作逻辑,页面变化时只需修改页面对象类,而不需要修改测试用例。
- 可读性:测试用例更加简洁,易于理解。
- 可重用性:页面对象可以在多个测试用例中重用
1.3 POM 的扩展
随着项目的复杂性增加,可以进一步扩展 POM 模式:
- Base Page:创建一个基础页面类,封装一些常用的操作方法和属性,其他页面类继承这个基础类。
- 组件对象:对于页面上的重复组件(如导航栏、页脚等),可以创建组件对象类,封装组件的操作方法。
- 页面工厂:使用页面工厂模式创建页面对象,简化页面对象的实例化过程。
1.4 示例
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_input = (By.ID, "username")
self.password_input = (By.ID, "password")
self.login_button = (By.ID, "login")
def enter_username(self, username):
self.driver.find_element(*self.username_input).send_keys(username)
def enter_password(self, password):
self.driver.find_element(*self.password_input).send_keys(password)
def click_login(self):
self.driver.find_element(*self.login_button).click()
2. selenium
2.1 selenium介绍
selenium是一种UI自动化工具,包含:
- selenium IDEA:自动录制脚本
- Webdriver:实现不同浏览器驱动,从而操纵浏览器的行为
- selenium grid:允许在不同的机器上并行运行不同的浏览器
2.2 安装
pip install selenium
2.3 浏览器操作
选择浏览器驱动
driver = webdriver.Chrome()
进入网页
driver.get("https://www.baidu.com/")
网页最大/小化
driver.maximize_window()
driver.minimize_window()
获取当前标签页的title
driver.title
获取当前标签页的url
driver.current_url
前进
driver.forward()
后退
driver.back()
刷新
driver.refresh()
关闭
driver.close()
滚动到顶部
driver.execute_script("window.scrollTo(0, 0);")
滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
鼠标悬停
ActionChains(driver).move_to_element(ele).perform()
寻找元素
driver.find_element(定位方式, '元素定位值')
寻找元素集
driver.find_elements(定位方式,)
2.4 元素定位
这里只介绍通过By方式定位,并且使用显式等待法,等待该元素加载出来。
import time
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")
driver.maximize_window()
ele = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.ID, 's-top-loginbtn')))
ele.click()
time.sleep(1)
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.ID, 'TANGRAM__PSP_11__userName')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.NAME, 'userName')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.CLASS_NAME, 'pass-text-input.pass-text-input-userName')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.XPATH, '//*[@id="TANGRAM__PSP_11__userName"]')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.XPATH, '/html/body/div[6]/div[2]/div[2]/div/div/div/div/div/div[1]/div[2]/form/p[3]/input[2]')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.LINK_TEXT, 'xxxxx')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, 'xxxxx')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#TANGRAM__PSP_11__userName')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[name="userName"]')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.pass-text-input.pass-text-input-userName')))
ele1 = WebDriverWait(driver,20).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.pass-text-input.pass-text-input-userName')))
# 定位用户输入框
# input_box = driver.find_element(By.ID, 'auto-id-1723364062172')
ele1.send_keys("1222222222")
time.sleep(1)
2.4 元素操作
元素点击
ele1.click()
获取元素属性
ele1.get_attribute('属性名字')
元素清空
ele1.clear()
表单提交
ele1.submit()
元素是否被选择
ele1.is_selected()
元素是否被使能
ele1.is_enabled()
输入框输入
ele1.send_keys("txt")
2.5 元素等待
Selenium 等待
(1)强制等待
操作:time.sleep(3)
缺点:强制等待,不管在规定时间内是否执行完成,都在等待
(2)隐式等待
优点:整个周期只需要设置一次,在规定时间内,整个网页都加载完成,才能执行下一步,否则会抛出异常。相当于全局设定。
缺点:必须等待整个页面加载完成才能下一步,有些非必要元素加载较慢,则会影响执行时间。
操作:driver.implicitly_wait(20)
(3)显式等待:
优点:给局部元素设置等待时间,定位到之后可以执行下一步。
操作:element = WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located((By.ID, "kw")))
3. pytest
3.1 安装
pip install pytest
3.2 命名方式
文件命名
测试文件通常以 test_ 开头或 _test 结尾。例如:
test_example.py
example_test.py
函数命名
测试函数应以 test_ 开头。例如:
def test_addition():
def test_subtraction():
类命名
类名应以 Test 开头,但不需要继承任何类。不能有__init__方法。例如:
class TestMathOperations:
def test_addition(self):
assert 1 + 1 == 2
def test_subtraction(self):
assert 2 - 1 == 1
3.3 夹具pytest
Fixtures
作用:用于在测试函数运行之前准备一些必要的环境或数据。在yield之前执行测试函数的前置操作,比如登陆等。接着使用yeild执行具体的测试函数。最后执行后置操作,清除数据等。
@pytest.fixture(scope="function", autouse=True)
def setup_teardown(self, request, driver):
print("============执行前置操作===========")
yield
print("============执行后置操作===========")
driver.delete_all_cookies()
driver.refresh()
print("清空cookies,退出登录状态")
参数化驱动测试用例:
login_data = [
('user1', '12345678', True),
('user2', '12345678', False),
]
具体测试函数:
@pytest.mark.parametrize("username,pwd,expected", login_data)
def test_login(self, username, pwd, expected):
print(f"Testing with: {username}, {pwd}, {expected}")
xxxxx具体执行
assert flag == expected, "用户登陆测试失败"
3.4 confest.py
该文件和测试用例属于同一目录,测试执行前会先执行该文件。名称固定,不能修改。
3.5 allure报告
可以安装allure输出报告
4. 项目结构
├── Logs:日志文件
├── common:公共方法
│ ├── __init__.py
│ ├── base_page.py
│ └── log.py
├── config:基础配置,数据库、用户名等
│ └── config.yaml
├── doc:接口文档
├── tests:测试用例
│ ├── __init__.py
│ ├── conftest.py
│ ├── data:测试数据文件夹
│ └── test_login_pom.py
├── pages:页面封装对象
│ ├── __init__.py
│ ├── login_page.py
│ └── home_page.py
├── pytest.ini:pytest参数配置
├── readme.md
├── reports:allure报告
│ └── allure-results
├── requirements.txt
├── utils:基础方法
│ └── load_data.py