这是初版的 pom web 测试框架,目录如下同时部分代码也放在下面,详细代码可前往 github 查看,欢迎大家给出宝贵意见。
|--base
| base_page.py(封装方法)
|
|--config
| allure_config.py(测试报告配置)
|
|--data
| code(验证码)
| user.yaml(用户目录)
|
|--logs
| log(日志文件)
| log.py(日志模块)
|
|--page_object
| login_page.py(登陆页面元素及流程)
|
|--reports(测试报告存放处)
| allure-page
| report
|
|--screenshot
| err_screenhot(错误截图)
| screenshot.py(截图模块)
|
|--test_case
| conftest(fixture配置)
| test_login.py(登录测试用例)
|
|--utils
| util(公共方法,类似于验证码识别等)
| web_driver.py(driver配置)
|
|--run.py(测试框架运行模块)
base(封装方法)
class BasePage:
def __init__(self, driver):
self.driver = driver
self.logger = Logs.get_logger()
self.scr = Screenshot(driver)
"""打开url"""
def open_url(self, url: str):
self.driver.maximize_window()
self.logger.info('最大化窗口')
self.driver.get(url)
self.logger.info(f'打开网址:{url}')
# self.driver.implicitly_wait(80)
"""元素定位,元组形态,返回web_element对象"""
def locator(self, loc: tuple) -> WebElement:
try:
# 显示等待直到元素可见并可交互
return self.display_wait(loc)
except NoSuchElementException:
self.logger.error(f'元素{loc}未找到')
self.scr.screenshot(loc)
raise
except TimeoutException:
self.logger.error(f'元素{loc}未在规定时间内变为可见')
self.scr.screenshot(loc)
raise
config(测试报告配置)
# 用户数据路径
user_data_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'user.yaml')
# 测试用例路径
def case_path(path):
return os.path.join(os.path.dirname(path), 'test_cases')
# 测试报告路径
def report_path(path):
return os.path.join(os.path.dirname(path), 'reports')
# 执行 pytest 命令生成 Allure 报告配置
pytest_command = [
"pytest",
"test_login.py", # 登陆测试
"test_home.py", # 首页测试
"test_search.py", # 搜索测试
"--alluredir=../reports/report",
"--clean-alluredir"]
# 执行 Allure 命令生成并打开报告配置
allure_command = ["allure", "generate", "report", "--clean"]
# 打开allure报告
allure_open = ["allure", "open", "allure-report"]
logs(日志)
class Logs:
LOG_DIRECTORY = os.path.join(os.path.dirname(__file__), 'log_file')
LOG_FORMAT = '[%(asctime)s] %(levelname)s %(name)s(%(lineno)d): %(message)s'
LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
LOG_LEVEL = logging.INFO
BACKUP_COUNT = 0
_logger = None
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.ensure_log_directory()
@classmethod
def ensure_log_directory(cls):
if not os.path.exists(cls.LOG_DIRECTORY):
os.makedirs(cls.LOG_DIRECTORY)
@classmethod
def get_logger(cls):
if cls._logger is None:
try:
cls.ensure_log_directory()
log_path = os.path.join(cls.LOG_DIRECTORY, f'{time.strftime("%Y%m%d")}.log')
file_handler = TimedRotatingFileHandler(log_path, when="midnight", interval=1,
encoding='utf8', backupCount=cls.BACKUP_COUNT)
formatter = logging.Formatter(cls.LOG_FORMAT, datefmt=cls.LOG_DATE_FORMAT)
file_handler.setFormatter(formatter)
cls._logger = logging.getLogger(cls.__name__)
cls._logger.setLevel(cls.LOG_LEVEL)
cls._logger.addHandler(file_handler)
except Exception as e:
raise RuntimeError(f"无法初始化类的记录器 '{cls.__name__}': {e}")
return cls._logger
page_object(页面对象)
class LoginPage(BasePage):
# 网址
_url = 'http://baidu.com'
# 账号
_account = ('id', 'account')
# 密码
_password = ('id', 'PASSWORD')
# 登录
_login_button = ('id', 'login_button')
def correct_login(self, username, password):
with allure.step("打开登陆页面"):
self.open_url(self._url)
with allure.step(f"输入账号:{username}"):
self.input(self._account, username)
with allure.step(f"输入密码:{password}"):
self.input(self._password, password)
with allure.step("点击登陆"):
self.click(self._login_button)
screenshot(截图)
class Screenshot:
def __init__(self, driver):
self._driver = driver
self._screenshot_path = os.path.join(os.path.dirname(__file__), 'err_screenshot')
self._ensure_screenshot_directory()
self._logger = Logs.get_logger()
def _ensure_screenshot_directory(self):
try:
if not os.path.exists(self._screenshot_path):
os.makedirs(self._screenshot_path)
except Exception as e:
self._logger.error(f"创建屏幕截图目录时出错: {e}")
def screenshot(self, error_name):
date_time = datetime.now().strftime('%Y-%m-%d_%H.%M.%S')
screenshot_name = f'{error_name}_{date_time}.png'
screenshot_path = os.path.join(self._screenshot_path, screenshot_name)
try:
self._driver.get_screenshot_as_file(screenshot_path)
with open(screenshot_path, 'rb') as image_file:
image_data = image_file.read()
allure.attach(image_data, name=screenshot_name, attachment_type=allure.attachment_type.PNG)
self._logger.info(f"截图成功: {screenshot_path}")
except FileNotFoundError as e:
self._logger.error(f"保存截图时文件路径不存在: {e}")
except Exception as e:
self._logger.error(f"截取屏幕快照并保存时出错: {type(e).__name__}: {e}")
test_case(测试用例)
conftest
@pytest.fixture(scope="class")
def driver():
with allure.step("webdriver初始化"):
driver_instance = init_driver()
try:
yield driver_instance
finally:
with allure.step("退出浏览器"):
driver_instance.quit()
test_login.py
@allure.epic("xxx系统")
@allure.feature("首页登陆页面")
class TestLogin:
@pytest.fixture(autouse=True)
def setup(self, driver):
self._lp = LoginPage(driver)
@allure.title("正确账号登录")
@allure.description("验证正确账号可以登录首页")
@allure.severity(allure.severity_level.BLOCKER)
@pytest.mark.parametrize("login_account", yaml.safe_load(open(user_data_path))['login_data'])
def test_login(self, driver, login_account):
try:
self._lp.correct_login(login_account['CorrectAccount'], login_account['CorrectPassword'])
except Exception as e:
screenshot_taker = Screenshot(driver)
screenshot_taker.screenshot(f"登陆出错")
raise e
unitls
unil
未用到公共方法,暂未配置
web_driver
def init_driver():
options = Options()
# 可选配置项
options.page_load_strategy = 'eager' # 页面加载策略,默认为'normal',可选'eager'和'none'。
# options.add_argument('--headless') #无头模式
options.add_argument('--disable-gpu') # 禁用gpu
options.add_argument('--user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
'like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0"') # 用户代理
# options.add_argument('--incognito') # 隐身模式
options.add_argument('--disable-infobars') # 禁用信息栏(浏览器正在被自动化工具控制)
options.add_argument('--start-maximized') # 窗口最大化
options.add_argument('--ignore-certificate-errors') # 忽略证书错误
options.add_argument('--no-sandbox') # 禁用沙箱模式
# 设置浏览器首选项
prefs = {
'download.prompt_for_download': False, # 下载文件时是否提示保存对话框
'safebrowsing.enabled': True, # 是否启用安全浏览功能
'credentials_enable_service': False, # 是否启用保存密码提示
'profile.password_manager_enabled': False # 是否启用密码管理功能
}
options.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome(options=options)
return driver
run(运行文件)
# 初始化日志配置
logger = Logs.get_logger()
# 路径配置
BASE_DIR = os.path.dirname(__file__)
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_cases')
REPORT_DIR = os.path.join(BASE_DIR, 'reports')
def main():
try:
# 执行 pytest 命令生成 Allure 报告
logger.info("正在运行 pytest 以生成 Allure 报告...")
subprocess.run(pytest_command, cwd=TEST_CASE_DIR, shell=True)
# 执行 Allure 命令生成报告
logger.info("正在生成 Allure 报告...")
subprocess.run(allure_command, cwd=REPORT_DIR, shell=True)
# 打开 Allure 报告
logger.info("打开Allure报告...")
subprocess.run(allure_open, cwd=REPORT_DIR, shell=True)
except subprocess.CalledProcessError as e:
logger.error(f"发生错误: {e}")
exit(1)
if __name__ == "__main__":
main()