以下是一个基于Python的自动化测试代码框架示例,包含了 app_lib
(库模块,用于存放通用功能相关代码)、app_test
(测试用例相关模块)、config
(配置文件及配置读取相关部分)等模块,整体框架结构清晰,便于扩展和维护,可以根据实际需求进一步调整和完善。
一、项目目录结构
my_app_auto_test/
├── app_lib/
│ ├── base/
│ │ ├── __init__.py
│ │ ├── base_driver.py
│ │ └── base_page.py
│ ├── pages/
│ │ ├── __init__.py
│ │ └── main_page.py
│ └── operations/
│ ├── __init__.py
│ └── main_operations.py
├── app_test/
│ ├── __init__.py
│ ├── test_main.py
│ └── conftest.py
├── config/
│ ├── __init__.py
│ └── config.ini
└── run_tests.py
二、各部分详细介绍
1. app_lib
模块
这是整个自动化框架的核心库模块,存放了各类基础功能、页面操作相关以及对象库相关的代码,按照功能进一步细分了不同的子目录。
app_lib/base
子目录:
-
__init__.py
:使base
目录成为一个Python包,可用于导入相关模块,也可以在其中进行一些初始化操作或者模块级别的变量定义等(如果需要的话)。base_driver.py
:用于初始化自动化测试所需的驱动程序,例如在移动端自动化使用Appium或者Web自动化使用Selenium时,相关代码示例如下:
import appium.webdriver
import selenium.webdriver
class BaseDriver:
def __init__(self, platform):
if platform == "android":
# 配置Appium相关的Desired Capabilities用于安卓设备
desired_caps = {
"platformName": "Android",
"deviceName": "your_device_name",
"appPackage": "your_app_package",
"appActivity": "your_app_activity"
}
self.driver = appium.webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
elif platform == "ios":
# 配置Appium相关的Desired Capabilities用于iOS设备
desired_caps = {
"platformName": "iOS",
"deviceName": "your_device_name",
"bundleId": "your_bundle_id"
}
self.driver = appium.webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
elif platform == "web":
# 配置Selenium相关的WebDriver,以Chrome为例
self.driver = selenium.webdriver.Chrome()
else:
raise ValueError("Unsupported platform")
这段代码根据传入的平台类型(android
、ios
或 web
)来初始化对应的驱动对象,用于后续与目标应用(移动应用或Web应用)进行交互。
base_page.py
:定义了页面操作的一些基础类和方法,比如通用的元素查找方法、等待元素加载方法等,示例如下:
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
class BasePage:
def __init__(self, driver):
self.driver = driver
def find_element(self, by, value, timeout=10):
"""
根据定位方式和定位值查找元素,等待元素出现,超时时间默认为10秒
"""
wait = WebDriverWait(self.driver, timeout)
return wait.until(EC.presence_of_element_located((by, value)))
这里的 BasePage
类接收一个驱动对象作为参数,然后提供了 find_element
方法用于在页面上查找元素,通过 WebDriverWait
机制等待元素出现,确保操作的稳定性。
app_lib/pages
子目录:
-
__init__.py
:同样是使pages
目录成为Python包,便于导入其中的页面类。main_page.py
:针对具体的应用主页面定义元素定位信息以及相关的获取元素方法等,例如对于一个视频编辑应用的主页面:
from app_lib.base.base_page import BasePage
from selenium.webdriver.common.by import By
class MainPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.edit_button = (By.XPATH, "//button[@id='edit_btn']")
self.start_create_button = (By.XPATH, "//button[@id='start_create_btn']")
def find_edit_button(self):
return self.find_element(*self.edit_button)
def find_start_create_button(self):
return self.find_element(*self.start_create_button)
在这个类中,继承自 BasePage
类,定义了主页面上“编辑按钮”和“开始创作按钮”的 XPATH
定位信息,并提供了对应的获取元素方法,方便后续操作层调用进行点击等操作。
app_lib/operations
子目录:
-
__init__.py
:使operations
目录成为Python包。main_operations.py
:基于页面类中获取到的元素,定义具体的操作方法,实现对页面元素的交互功能,示例如下:
from app_lib.pages.main_page import MainPage
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
class MainOperations:
def __init__(self, driver):
self.main_page = MainPage(driver)
def click_edit_button(self):
"""
点击编辑按钮的操作方法
"""
edit_button = self.main_page.find_edit_button()
edit_button.click()
def click_start_create_button(self):
"""
点击开始创作按钮的操作方法
"""
start_create_button = self.main_page.find_start_create_button()
start_create_button.click()
def input_text(self, element_locator, text):
"""
向指定元素输入文本内容
:param element_locator: 元素的定位信息,格式为 (By.定位方式, 定位表达式),例如 (By.ID, "input_box_id")
:param text: 要输入的文本内容
"""
element = self.main_page.find_element(*element_locator)
element.clear() # 先清空原有内容(如果有)
element.send_keys(text)
def get_element_text(self, element_locator):
"""
获取指定元素的文本内容
:param element_locator: 元素的定位信息,格式为 (By.定位方式, 定位表达式),例如 (By.ID, "text_element_id")
:return: 元素的文本内容,如果元素不存在或获取失败返回None
"""
try:
element = self.main_page.find_element(*element_locator)
return element.text
except:
return None
def is_element_visible(self, element_locator, timeout=10):
"""
判断指定元素是否可见
:param element_locator: 元素的定位信息,格式为 (By.定位方式, 定位表达式),例如 (By.ID, "visible_element_id")
:param timeout: 等待元素可见的超时时间,默认为10秒
:return: True如果元素在超时时间内可见,False则表示不可见
"""
try:
wait = WebDriverWait(self.main_page.driver, timeout)
wait.until(EC.visibility_of_element_located(element_locator))
return True
except:
return False
def wait_for_element_to_be_clickable(self, element_locator, timeout=10):
"""
等待指定元素变为可点击状态
:param element_locator: 元素的定位信息,格式为 (By.定位方式, 定位表达式),例如 (By.ID, "clickable_element_id")
:param timeout: 等待元素可点击的超时时间,默认为10秒
:return: 可点击的元素对象,如果超时未变为可点击状态则返回None
"""
try:
wait = WebDriverWait(self.main_page.driver, timeout)
return wait.until(EC.element_to_be_clickable(element_locator))
except:
return None
def scroll_to_element(self, element_locator):
"""
将页面滚动到指定元素位置(适用于移动端或有滚动条的Web页面情况)
:param element_locator: 元素的定位信息,格式为 (By.定位方式, 定位表达式),例如 (By.ID, "scrolled_element_id")
"""
# 以下是一种简单的滚动逻辑示例,不同的驱动(如Appium、Selenium针对不同浏览器)可能有不同的实现方式
# 这里假设是基于JavaScript在Web页面上实现滚动,对于移动端也可以通过Appium提供的相关滚动方法来替换
element = self.main_page.find_element(*element_locator)
self.main_page.driver.execute_script("arguments[0].scrollIntoView();", element)
time.sleep(1) # 稍作停顿,确保滚动效果生效(可根据实际情况调整停顿时间)
在上述代码中:MainOperations
类接收驱动对象,实例化 MainPage
类,然后定义了点击编辑按钮和点击开始创作按钮的操作方法,将页面元素查找和实际操作进行了封装整合。
input_text
方法用于向指定的页面元素(比如输入框)中输入文本内容,先清空原有内容再通过send_keys
方法输入新的文本。get_element_text
方法用于获取页面元素展示的文本内容,通过查找元素后返回其text
属性获取文本,如果查找过程出现异常则返回None
。is_element_visible
方法借助WebDriverWait
和EC.visibility_of_element_located
条件来判断元素在指定的超时时间内是否可见,返回对应的布尔值结果。wait_for_element_to_be_clickable
方法等待元素变为可点击状态,同样使用WebDriverWait
机制,超时未可点击则返回None
,可点击时返回对应的元素对象,方便后续进行点击操作等。scroll_to_element
方法实现将页面滚动到指定元素位置,示例中给出了一种基于 JavaScript 在 Web 页面上滚动的简单逻辑,对于移动端可根据 Appium 相关的滚动功能进行相应替换,滚动后还添加了短暂的停顿时间以确保滚动效果生效。
这些操作方法可以在自动化测试用例中灵活组合使用,更加全面地模拟用户在应用页面上的各种交互行为,对页面元素进行操作、判断和处理,以验证应用功能是否符合预期。
2. app_test
模块
用于存放自动化测试用例以及相关的测试配置和夹具(fixture)等代码。
__init__.py
:使app_test
目录成为Python包。test_main.py
:编写具体的自动化测试用例,使用pytest
测试框架(这里假设使用pytest
,你也可以选择其他测试框架如unittest
等),示例如下:
import pytest
from app_lib.operations.main_operations import MainOperations
@pytest.fixture
def setup_driver():
# 这里可以调用app_lib/base/base_driver.py中的BaseDriver类初始化驱动对象
# 假设初始化一个移动端安卓的驱动,你可以根据实际需求修改平台类型
driver = BaseDriver("android").driver
yield driver
driver.quit()
def test_start_creation(setup_driver):
main_operations = MainOperations(setup_driver)
main_operations.click_edit_button()
main_operations.click_start_create_button()
在这个测试用例中,首先通过 setup_driver
夹具函数初始化并获取驱动对象,然后利用 MainOperations
类的实例对页面上的按钮进行点击操作,模拟用户在应用主页面上进行“编辑”后“开始创作”的操作流程,验证功能是否正常。
conftest.py
:用于定义一些全局的测试夹具或者配置,这些夹具可以在多个测试用例中共享使用,例如可以在这里配置测试的全局环境变量、设置测试的超时时间等,示例(简单配置setup_driver
夹具在不同测试模块中的共享情况):
import pytest
from app_lib.base.base_driver import BaseDriver
@pytest.fixture(scope="module")
def setup_driver():
# 初始化驱动对象,这里同样可以根据实际需求调整平台类型等配置
driver = BaseDriver("android").driver
yield driver
driver.quit()
这里将 setup_driver
夹具的作用域设置为 module
(模块级别),使得在同一个测试模块中的多个测试用例都可以共享使用这个夹具获取到的驱动对象,避免重复初始化驱动带来的资源浪费和可能出现的问题。
3. config
模块
用于存放配置文件以及配置读取相关的代码,便于统一管理应用自动化测试过程中的各种配置信息。
__init__.py
:使config
目录成为Python包。config.ini
:配置文件示例,采用常见的INI
格式,内容可以包含如应用相关的信息、测试环境相关的配置等,例如:
[Appium]
server_url = http://127.0.0.1:4723/wd/hub
platform = android
[Android]
deviceName = emulator-5554
appPackage = com.example.app
appActivity = com.example.app.MainActivity
[Web]
base_url = https://www.example.com
这里定义了 Appium
相关的服务器地址和平台类型,安卓设备的具体名称、应用的包名和启动Activity等信息,以及如果是Web应用时的基础访问网址等配置内容,在代码中可以通过相应的配置读取模块来获取这些配置值进行使用。
4. run_tests.py
这是一个用于启动自动化测试的入口脚本,可以根据实际需求配置测试的执行方式、选择要执行的测试用例范围等,示例如下:
import pytest
if __name__ == "__main__":
pytest.main(["-v", "-s", "app_test"])
这段代码使用 pytest
的 main
函数启动测试,通过传入参数 -v
(显示详细的测试执行信息)和 -s
(允许在测试执行过程中输出标准输出信息,比如打印一些调试语句等)以及指定测试用例所在的目录 app_test
,来运行所有位于 app_test
目录下的自动化测试用例。
通过这样的代码框架结构,将自动化测试相关的功能按照不同的职责进行了清晰的划分,app_lib
提供核心的功能支持,app_test
编写具体的测试用例,config
管理配置信息,run_tests.py
启动测试,各部分协同工作,方便在不同的应用自动化测试场景下进行扩展和维护,提高测试效率和代码质量。