文章目录
一.概要
完成餐饮后台管理系统的测试报告
二.功能测试
完成测试用例的编写,这里我们使用思维导图完成。
2.1 测试用例编写
2.2 存在的缺陷
已完成的订单存在取消选项
首页图片不显示
修改之后扔存在缺陷
退出登录后仍然保存着账号密码
三.WebUI自动化测试
3.0 相关配置环境
Python 3.9
allure 2.29.0
Pytest 8.3.2
Selenium 4.23.0
3.1 框架整体搭建
这里我们使用Python+Selenium+Pytest来搭建框架,整体示意图如下:
文件整体目录:
带*号在PyCharm中是新建Python软件包
3.2 配置模块(Conf)
首先在conf文件夹下面创建 config.ini, config.py文件
3.2.1 日志相关
在config.ini中进行配置日志相关的一些信息
日志相关的配置
filename: 指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中;
filemode: 文件打开方式,在指定了filename时使用这个参数,默认值为“w”还可指定为“a”;
format: 指定handler使用的日志显示格式;
datefmt: 指定日期时间格式。, 格式参考strftime时间格式化(下文)
level: 设置rootlogger的日志级别
stream: 用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。 若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化信息:
参数 | 作用 |
---|---|
%(name)s | Logger的名字 |
%(levelno)s | 数字形式的日志级别 |
%(levelname)s | 文本形式的日志级别 |
%(pathname)s | 调用日志输出函数的模块的完整路径名,可能没有 |
%(filename)s | 调用日志输出函数的模块的文件名 |
%(module)s | 调用日志输出函数的模块名 |
%(funcName)s | 调用日志输出函数的函数名 |
%(lineno)d | 调用日志输出函数的语句所在的代码行 |
%(created)f | 当前时间,用UNIX标准的表示时间的浮 点数表示 |
%(relativeCreated)d | 输出日志信息时的,自Logger创建以 来的毫秒数 |
%(asctime)s | 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 |
%(thread)d | 线程ID。可能没有 |
%(threadName)s | 线程名。可能没有 |
%(process)d | 进程ID。可能没有 |
%(message)s | 用户输出的消息 |
3.2.2 邮件相关
使用python自带的smtplib和email模块,相关配置如下:
3.2.3 config.py相关
接下来就是config.py里面的一些内容了,在这个文件里面我们读取config.ini用于设置和导入基本的配置文件,置备目录路径,并在其余部分的脚本中使用这些预配置信息,以便定制属于自己项目结构和需求的参数和路径设置。
我们对他进行打印,输出读取的配置文件和相关路径
3.3日志模块(Comm)
前面我们对日志文件进行了配置和简单介绍,这里我们来进行编写代码,创建文件log.py在‘Comm’文件夹下面,为什么会放到Comm文件夹下呢,因为log文件是一个通用的模块,而原本的Log文件夹下存放的是我们要输出的日志相关的一些信息
3.4邮件模块(Comm)
邮件模块就不多赘述了,网上一堆,直接搜一下,学习一下,也可以直接cv一下,拿过来用就行,配置的话之前已经配置完了,代码:
3.6 Pytest相关配置(pytest.ini)
因为我们用到的是pytest的框架,所以我们对他进行了一些配置,如图
3.7 UI自动化(Canyin)
这里我们使用PO设计模式,好处是通过对界面元素的封装减少冗余代码,主要体现在对界面交互细节的封装,也就是在实际测试中只关注业务流程;同时在后期维护中,若元素定位发生变化, 只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。
3.7.1 base_page.py
在Canyin文件夹下新建pages文件夹,用于存放界面元素,创建base_page对selenium的基础操作进行二次封装,这里给出两种封装操作,因为不想过度封装,本文用的第一种,个人推荐第二种
# 第一种
from selenium import webdriver
from selenium.webdriver import ActionChains
class BasicPage(object):
# 构造函数,用于创建类的新实例(或对象)
def __init__(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(5)
# 点击网页上的某个元素。
# `elem["by"]` 是一个元组,包含了定位元素的方法和值
def click(self, elem):
self.driver.find_element(*elem["by"]).click()
# 使用`find_element`方法查找元素并输入。
def input(self, elem, value):
e = self.driver.find_element(*elem["by"])
e.clear()
e.send_keys(value)
def input_img(self, elem, value):
e = self.driver.find_element(*elem["by"])
e.send_keys(value)
# - 这个方法用于获取页面元素的属性值。
def get_value(self, elem):
return self.driver.find_element(*elem["by"])
# 找出所有的标签,返回获取到的标签元素 list
def get_values(self, elems):
return self.driver.find_elements(*elems["by"])
# 找出所有的标签,返回获取到的标签元素 list
def get_text(self, elem):
return self.driver.find_element(*elem["by"]).text
# 找出所有的标签,返回获取到的标签元素 list
def get_mose(self, el):
ActionChains(self.driver).move_to_element(el).perform() # 鼠标悬停
# 返回url
def get_url(self):
return self.driver.current_url
def get_js(self, js):
self.driver.execute_script(js)
# 设置等待时间
def get_wait(self, time=5):
self.driver.implicitly_wait(time)
def quit(self):
self.driver.quit()
#第二种
import time
from selenium.webdriver.common.by import By
from selenium import webdriver
import os
import importlib
import logging
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
SimpleActions = ['clear()', 'send_keys()', 'click()', 'submit()', 'size', 'text', 'is_displayed()', 'get_attribute()']
logger = logging.getLogger('main.page')
class Page(object):
def __init__(self, driver, page):
self.driver = driver
self.page = page
self.elements = get_page_elements(page)
self.by = ()
self.action = None
self.driver.implicitly_wait(10)
def _get_page_elem(self, elem):
# 获取定位的元素by和操作 action
for echo in self.elements:
if echo['name'] == elem:
self.by = echo['by']
if 'action' in echo and echo['action'] is not None:
self.action = echo['action']
else:
self.action = None
def _selenium_cmd(self, find_type='find_element', args=None):
#拼接命令
cmd = 'self.driver.' + find_type + '(*self.by)'
if self.action:
if self.action in SimpleActions:
cmd = cmd + '.' + self.action
if args:
cmd = cmd[:-1] + 'args' + ')'
return cmd
def oper_elem(self, elem, args=None):
self._get_page_elem(elem)
cmd = self._selenium_cmd('find_element', args)
return eval(cmd)
def oper_elems(self, elem, args=None):
self._get_page_elem(elem)
cmd = self._selenium_cmd('find_elements', args)
return eval(cmd)
def get_page_elements(page):
"""动态加载页面定义文件,获取文件中定义的元素列表elements"""
elements = None
if page:
try:
m = importlib.import_module(page)
elements = m.elements
except Exception as e:
logger.error("错误哦:%s" % e)
return elements
3.7.2 login.py
from selenium.webdriver.common.by import By
# 登录
login_url = 'http://120.25.124.200/#/login'
login_elements = {
"inputs": {'by': (By.CLASS_NAME, u'el-input__inner'), "get_attribute": None},
"button": {'by': (By.CSS_SELECTOR, u'.el-button--primary.el-button--medium'), "get_attribute": "click()"},
}
3.7.3 function_menu.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 工作台和数据统计
f_menu_elements = {
# 总菜单
"menu": {'by': (By.CLASS_NAME, u'el-menu-item'), "get_attribute": None},
# 工作台-数据
"data": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[1]/h2/span'), "get_attribute": 'click()'},
# 工作台-订单
"ord": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/h2/span'), "get_attribute": 'click()'},
# 工作台-菜品
"dis": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div[1]/h2/span'),
"get_attribute": 'click()'},
# 工作台-新增菜品
"add_dis": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div[1]/div/ul/li[3]'),
"get_attribute": 'click()'},
# 工作台-套餐
"meal": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div[2]/h2/span'),
"get_attribute": 'click()'},
# 工作台-新增套餐
"add_meal": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div[2]/div/ul/li[3]'),
"get_attribute": 'click()'},
# 数据统计-日期切换
"M_D_Y": {'by': (By.CLASS_NAME, u'li-tab'), "get_attribute": None},
# 数据统计-获取时间
"get_M_D_Y": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[1]/div[2]/p'), "get_attribute": 'innerText'},
# 数据统计-数据导出
"get_Data": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[1]/button/i'),
"get_attribute": 'click()'},
# 数据统计-确认导出
"On": {'by': (By.CSS_SELECTOR, u'.el-button.el-button--default.el-button--small.el-button--primary '),
"get_attribute": 'click()'},
# 数据统计-验证下载
"down": {'by': (By.XPATH, u'/html/body/a'), "get_attribute": None},
}
3.7.4 menu_setmeal.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 套餐管理
setmeal_elements = {
# 套餐名称
"name": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[2]/div[3]/table/tbody/tr/td[2]')},
# 3种搜索
"select": {'by': (By.CLASS_NAME, u'el-input__inner')},
# 售卖类型
"list": {'by': (By.CLASS_NAME, u'el-scrollbar__view.el-select-dropdown__list')},
"list1": {'by': (By.CLASS_NAME, u'el-select-dropdown__item')},
# 确认按钮
"button": {'by': (By.CLASS_NAME, u'el-button.normal-btn.continue.el-button--default')},
# 套餐分类
"type": {'by': (By.CLASS_NAME, u'el-table__row')},
# 清除
"clean": {'by': (By.CLASS_NAME, u'el-input__clear')},
"clean_lst": {'by': (By.CLASS_NAME, u'el-icon-circle-close')},
# 修改按钮
"update": {'by': (By.CLASS_NAME, u'el-button.blueBug.el-button--text.el-button--small')},
# 售卖状态
"stau": {'by': (By.CLASS_NAME, u'tableColumn-status')},
# 删除
"delete": {'by': (By.CLASS_NAME, u'delBut.el-button--text.el-button--small')},
# 确认删除
"yes_del": {'by': (By.CLASS_NAME, u'el-button.el-button--default.el-button--small.el-button--primary')},
# 删除成功
"yes": {'by': (By.CLASS_NAME, u'el-message__icon.el-icon-success')},
# 删除失败
"no": {'by': (By.CLASS_NAME, u'el-message__icon.el-icon-error')},
# 新建套餐
"new": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[1]/div[4]/button')},
"input": {'by': (By.CLASS_NAME, u'el-input__inner')},
"item": {'by': (By.CLASS_NAME, u'el-select-dropdown__item')},
"addBut": {'by': (By.CLASS_NAME, u'addBut')},
"box": {'by': (By.CLASS_NAME, u'el-checkbox__inner')},
"add": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div/div[3]/span/button[2]')},
# 上传图片
"img": {'by': (By.NAME, u'file')},
# 保存
"save": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/form/div[6]/div/div/button[2]')},
"qx": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/form/div[6]/div/div/button[1]')},
}
3.7.5 menu_order.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 订单管理
order_elements = {
# 订单状态
"list": {'by': (By.CLASS_NAME, u'tab-item'), "get_attribute": None},
# 查询按钮
"button": {'by': (By.CLASS_NAME, u'el-button.normal-btn.continue.el-button--default'),
"get_attribute": "click()"},
# 输入订单号
"input_ord": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[1]/div[1]/input'),
"get_attribute": 'send_key()'},
# 输入手机号
"input_pho": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[1]/div[2]/input'),
"get_attribute": 'send_key()'},
# 选择下单时间
"star_input": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[1]/div[3]/input[1]')},
"end_input": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[1]/div[3]/input[2]')},
"ing_value": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr[1]/td[6]/div')},
# 获取订单号
"get_ord": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr[1]/td[1]/div'),
"get_attribute": 'text'},
"get_ord1": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr/td[1]/div'),
"get_attribute": 'text'},
# 获取手机号
"get_pho": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr[1]/td[4]/div'),
"get_attribute": 'text'},
"get_pho1": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr/td[4]/div'),
"get_attribute": 'text'},
"get_pho2": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div[2]/div[3]/table/tbody/tr[1]/td[3]/div'),
"get_attribute": 'text'},
# 获取列下的所有数据
"row": {'by': (By.CLASS_NAME, u'el-table__row')},
# 查看按钮
"check": {'by': (By.CLASS_NAME, u'el-button.blueBug.non.el-button--text')},
# 订单号
"t_ord": {'by': (
By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[2]/div/div[1]/div/div[1]/div/div[1]/div')},
# 用户名
"t_usr": {'by': (By.XPATH,
u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/div[1]/span')},
# 手机号码
"t_pho": {'by': (By.XPATH,
u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[2]/div/div[1]/div/div[2]/div[1]/div['
u'1]/div[2]/span')},
# 用户地址
"t_address": {'by': (By.XPATH,
u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[2]/div/div[1]/div/div[2]/div[1]/div['
u'1]/div[3]/span')},
# 下单时间
"t_time": {
'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[2]/div/div[1]/div/div[1]/p')},
# 取消操作
"取消": {'by': (By.CLASS_NAME, u'el-button.delBut.el-button--text')},
# 输入
"输入": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[4]/div/div[2]/form/div/div/div/div/input')},
"确定": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[4]/div/div[3]/span/button[2]')},
# 弹窗返回
"return": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[3]/div/div[1]/button/i')},
}
3.7.6 menu_dish.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 菜品管理
dish_elements = {
# 菜品名称
"name": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[2]/div[3]/table/tbody/tr/td[2]')},
# 3种搜索
"select": {'by': (By.CLASS_NAME, u'el-input__inner')},
# 售卖类型
"list": {'by': (By.CLASS_NAME, u'el-scrollbar__view.el-select-dropdown__list')},
"list1": {'by': (By.CLASS_NAME, u'el-select-dropdown__item')},
# 确认按钮
"button": {'by': (By.CLASS_NAME, u'el-button.normal-btn.continue.el-button--default')},
# 菜品分类
"type": {'by': (By.CLASS_NAME, u'el-table__row')},
# 清除
"clean": {'by': (By.CLASS_NAME, u'el-input__clear')},
"clean_lst": {'by': (By.CLASS_NAME, u'el-icon-circle-close')},
# 修改按钮
"update": {'by': (By.CLASS_NAME, u'el-button.blueBug.el-button--text.el-button--small')},
# 售卖状态
"stau": {'by': (By.CLASS_NAME, u'tableColumn-status')},
# 删除
"delete": {'by': (By.CLASS_NAME, u'delBut.el-button--text.el-button--small')},
# 确认删除
"yes_del": {'by': (By.CLASS_NAME, u'el-button.el-button--default.el-button--small.el-button--primary')},
# 删除成功
"yes": {'by': (By.CLASS_NAME, u'el-message__icon.el-icon-success')},
# 删除失败
"no": {'by': (By.CLASS_NAME, u'el-message__icon.el-icon-error')},
# 新建菜品
"new": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[1]/div[4]/button')},
"input": {'by': (By.CLASS_NAME, u'el-input__inner')},
"item": {'by': (By.CLASS_NAME, u'el-select-dropdown__item')},
"addBut": {'by': (By.CLASS_NAME, u'addBut')},
"box": {'by': (By.CLASS_NAME, u'el-checkbox__inner')},
"add": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div[2]/div/div[3]/span/button[2]')},
# 上传图片
"img": {'by': (By.NAME, u'file')},
# 保存
"save": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/form/div[6]/button[2]')},
"qx": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/form/div[6]/button[1]')},
}
3.7.7 menu_category.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 分类管理
category_elements = {
# 分类名称
"name": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[2]/div[3]/table/tbody/tr/td[2]')},
# 3种搜索
"select": {'by': (By.CLASS_NAME, u'el-input__inner')},
# 售卖类型
"list": {'by': (By.CLASS_NAME, u'el-scrollbar__view.el-select-dropdown__list')},
# 确认按钮
"button": {'by': (By.CLASS_NAME, u'el-button.normal-btn.continue.el-button--default')},
# 套餐分类
"type": {'by': (By.CLASS_NAME, u'el-table__row')},
# 清除
"clean": {'by': (By.CLASS_NAME, u'el-input__clear')},
"clean_lst": {'by': (By.CLASS_NAME, u'el-icon-circle-close')},
}
3.7.8 menu_employee.py
from selenium.webdriver.common.by import By
login_url = 'http://120.25.124.200/#/login'
# 员工管理
employee_elements = {
# 员工名称
"name": {'by': (By.XPATH, u'//*[@id="app"]/div/div[2]/section/div/div/div[2]/div[3]/table/tbody/tr/td[1]')},
# 搜索
"select": {'by': (By.CLASS_NAME, u'el-input__inner')},
# 确认按钮
"button": {'by': (By.CLASS_NAME, u'el-button.normal-btn.continue.el-button--default')},
}
3.8 测试用例(test_Case)
对pytest这一部分感兴趣的话,建议先去了解一下,再来看相关代码,或者关注我配合相关的知识更好,其实也蛮简单的。
3.8.1 conftest.py
一定要放在test_case文件夹下!!!
import time # 导入time模块,用于在测试中添加延时
import pytest # 导入pytest框架,用于编写和运行测试用例
from pytest_assume.plugin import assume # 导入pytest-assume插件中的assume函数,用于软断言
from canyin.canyi import CY # 从canyin包的canyi模块中导入CY类,用于操作应用程序
from canyin.pages.function_menu import f_menu_elements # 导入功能菜单的元素定位器
# 定义一个会话级别的测试夹具,用于初始化和配置测试环境
@pytest.fixture(scope="session")
def session_scope(usr='admin', psw='123456'):
# 创建CY类的实例,用于操作应用程序
a = CY()
# 打开应用程序
a.open()
# 使用用户名和密码登录应用程序,返回登录后的驱动对象
driver = a.login(usr, psw)
# 等待1秒,确保页面加载完成
time.sleep(1)
# 使用assume进行软断言,检查当前URL是否与预期的登录后URL匹配
# 如果不匹配,将记录一个软断言失败,但不会立即终止测试
assume(driver.current_url == 'http://120.25.124.200/#/dashboard', msg='登录成功')
# 使用yield返回CY实例,以便在测试用例中使用
yield a
3.8.2 test_login.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# ---------------------------------
# @FileName: test_login.py
# @Time: 2023/4/22 17:16
# @Author: sjc
# ---------------------------------
import os
import time
import allure # 导入Allure报告库,用于生成测试报告
import pytest # 导入pytest框架,用于编写和运行测试用例
from pytest_assume.plugin import assume # 导入pytest-assume插件中的assume函数,用于软断言
from selenium.webdriver.common.by import By # 导入Selenium的By类,用于定位元素
from canyin.canyi import CY # 从canyin包的canyi模块中导入CY类,用于操作应用程序
# 使用Allure装饰器定义测试用例的分类和特性
@allure.epic("餐饮后台")
@allure.feature("登录相关的用例")
class TestLogin:
# 使用Allure装饰器定义测试用例的标题和测试案例的链接
@allure.title('登录成功')
@allure.testcase('http://120.25.124.200/#/dashboard', '登录页面,可直接点击')
# 使用pytest的parametrize装饰器来参数化测试用例
@pytest.mark.parametrize("usr, psw", [("admin", "123456")])
def test_1(self, usr, psw):
""" 正确的账号密码,登录成功"""
a = CY() # 创建CY类的实例,用于操作应用程序
a.open() # 打开应用程序
driver = a.login(usr, psw) # 使用用户名和密码登录应用程序
time.sleep(1) # 等待1秒,确保页面加载完成
# 使用assume进行软断言,检查当前URL是否与预期的登录后URL匹配
assume(driver.current_url == 'http://120.25.124.200/#/dashboard', msg='登录成功')
driver.quit() # 关闭浏览器驱动
# 使用Allure装饰器定义测试用例的标题和测试案例的链接
@allure.title('登录失败')
@allure.testcase('http://120.25.124.200/#/dashboard', '登录页面,可直接点击')
# 使用pytest的parametrize装饰器来参数化测试用例
@pytest.mark.parametrize("usr, psw", [("admin", "roottt")])
def test_2(self, usr, psw):
""" 错误的账号密码,登录失败"""
a = CY() # 创建CY类的实例,用于操作应用程序
a.open() # 打开应用程序
driver = a.login(usr, psw) # 使用用户名和密码登录应用程序
# 使用assume进行软断言,检查当前URL是否与预期的登录页面URL匹配
assume(driver.current_url == 'http://120.25.124.200/#/login', msg='登录失败')
time.sleep(1) # 等待1秒,确保页面加载完成
# 寻找错误信息元素
a = driver.find_elements(by=By.CLASS_NAME, value='el-message--error')
# 使用assume进行软断言,检查是否找到了错误信息元素
assume(a is not None, msg=a[0].text if a else '没有找到错误信息')
driver.quit() # 关闭浏览器驱动
运行效果:
3.8.3 test_f_menu.py
import os
import time
import allure # 导入Allure报告库,用于生成测试报告
import pytest # 导入pytest框架,用于编写和运行测试用例
from pytest_assume.plugin import assume # 导入pytest-assume插件中的assume函数,用于软断言
from canyin.canyi import CY # 从canyin包的canyi模块中导入CY类,用于操作应用程序
from canyin.pages.function_menu import f_menu_elements # 导入功能菜单的元素定位器
# 定义两个URL列表,分别对应不同的功能页面
url_cs = ['dashboard', 'statistics', 'order', 'setmeal', 'dish', 'category', 'employee']
url_ym = ['statistics', 'order', 'dish', 'dish/add', 'setmeal', 'setmeal/add']
# 使用Allure装饰器定义测试用例的分类和特性
@allure.epic("餐饮后台")
@allure.feature("功能页面的切换,工作台和数据统计页面的测试")
class TestFMenu:
# 使用Allure装饰器定义测试用例的标题
@allure.title('功能页面')
def test_1(self, session_scope):
"""测试功能页面切换是否正常"""
menu = session_scope.get_values(f_menu_elements['menu']) # 获取菜单元素
assert menu != [], 'menu中标签数据获取失败' # 断言菜单元素不为空
for i in range(0, len(url_cs)):
menu[i].click() # 点击菜单项
time.sleep(1) # 等待1秒,确保页面跳转
# 使用assume进行软断言,检查当前URL是否与预期的URL匹配
assume(session_scope.get_url() == 'http://120.25.124.200/#/' + url_cs[i], msg=url_cs[i] + "跳转失败")
# 使用Allure装饰器定义测试用例的标题
@allure.title('工作台')
def test_2(self, session_scope):
"""测试工作台功能操作"""
menu = session_scope.get_values(f_menu_elements['menu']) # 获取菜单元素
assert menu != [], 'menu中标签数据获取失败' # 断言菜单元素不为空
# 转到工作台
menu[0].click()
elements = ['data', 'ord', 'dis', 'add_dis', 'meal', 'add_meal'] # 定义工作台操作的元素
for i in range(len(elements)):
session_scope.click(f_menu_elements[elements[i]]) # 点击工作台操作元素
time.sleep(1) # 等待1秒,确保页面跳转
assume(session_scope.get_url() == 'http://120.25.124.200/#/' + url_ym[i], msg=url_ym[i] + "跳转失败")
menu[0].click() # 返回工作台
time.sleep(1)
# 断言返回工作台后的URL是否正确
assert session_scope.get_url() == 'http://120.25.124.200/#/' + url_cs[0], url_ym[i] + "跳转失败-->" + url_cs[0]
# 使用Allure装饰器定义测试用例的标题
@allure.title('数据统计')
def test_3(self, session_scope):
"""测试数据统计功能"""
session_scope.get_wait() # 等待页面加载
# 获取菜单元素
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败' # 断言菜单元素不为空
# 转到数据统计
menu[1].click()
data = session_scope.get_values(f_menu_elements['M_D_Y']) # 获取日期选择元素
str1 = "当前时间: " + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n' # 记录当前时间
for i in range(len(data)):
data[i].click() # 点击日期选择元素
time.sleep(0.5) # 等待0.5秒,确保页面更新
p = session_scope.get_values(f_menu_elements['get_M_D_Y']) # 获取更新后的日期值
assume(p is not None, msg='日期获取失败') # 断言日期值不为空
str1 = str1 + data[i].text + '-->:' + p[0].text + '\n' # 记录日期切换情况
allure.attach(name='查看日期切换是否成功', body=str1) # 将记录的日期切换情况作为附件添加到Allure报告
# 点击下载按钮
session_scope.click(f_menu_elements['get_Data'])
session_scope.click(f_menu_elements['On'])
# 断言下载是否成功
assert session_scope.get_values(f_menu_elements['down']), '下载失败'
time.sleep(2) # 等待2秒,确保下载完成
运行效果:
3.8.4 test_menu_dish.py
import os
import random
import time
import allure # 导入Allure报告库,用于生成测试报告
import pytest # 导入pytest框架,用于编写和运行测试用例
from pytest_assume.plugin import assume # 导入pytest-assume插件中的assume函数,用于软断言
from selenium.webdriver.common.by import By # 导入Selenium的By类,用于定位元素
from canyin.pages.function_menu import f_menu_elements # 导入功能菜单的元素定位器
from canyin.pages.menu_dish import dish_elements # 导入菜品管理页面的元素定位器
# 使用Allure装饰器定义测试用例的分类和特性
@allure.epic("餐饮后台")
@allure.feature("菜品管理")
class TestMenuDish:
# 定义一个pytest fixture,用于设置测试前的准备工作
@pytest.fixture()
def comm(self, session_scope):
session_scope.get_wait() # 等待页面加载
# 获取菜单元素并点击菜品管理菜单项
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败'
menu[4].click()
# 使用Allure装饰器定义测试用例的标题
@allure.title('菜品查询')
def test_1(self, session_scope, comm):
"""测试菜品查询功能是否正常"""
text1 = session_scope.get_text(dish_elements['name']) # 获取菜品名称
# 点击输入框并输入菜品名称
a = session_scope.get_values(dish_elements['select'])
a[0].send_keys(text1)
time.sleep(1)
# 点击查询按钮
session_scope.click(dish_elements['button'])
text2 = session_scope.get_text(dish_elements['name'])
# 断言查询前后的菜品名称一致且不为空
assume(text1 == text2, msg='名称查询不正确')
# 清除输入框内容
session_scope.get_mose(a[0])
time.sleep(1)
session_scope.click(dish_elements['clean'])
flag = 0
for i in range(1, 3):
# 点击分类
a[i].click()
lst = session_scope.get_values(dish_elements['list'])
li = lst[~flag].find_elements(by=By.TAG_NAME, value='li')
for j in range(0, len(li)):
# 选择菜品类型
time.sleep(0.5)
text = li[j].text
li[j].click()
# 点击查询
session_scope.click(dish_elements['button'])
time.sleep(1)
# 验证查询结果是否正确
type_list = session_scope.get_values(dish_elements['type'])
for z in type_list:
td = z.find_elements(by=By.TAG_NAME, value='td')
if i == 2:
assume((td[5].text == text) or (td == []), '售卖状态不匹配')
# else:
# 这里可以添加其他状态的验证逻辑
# 清除选择
session_scope.get_mose(a[i])
session_scope.click(dish_elements['clean_lst'])
a[i].click()
# 使用 allure 框架为测试用例命名
@allure.title('菜品修改')
def test_2(self, session_scope, comm):
"""菜品修改"""
# 点击修改按钮,然后等待页面跳转
time.sleep(1)
session_scope.click(dish_elements['update'])
time.sleep(1)
# 获取当前页面的 URL 并断言是否跳转到了正确的地址
url = session_scope.get_url()
assume((url[0:36] == 'http://120.25.124.200/#/dish/add?id='), msg='跳转正确')
time.sleep(1)
# 点击保存按钮
session_scope.click(dish_elements['save'])
# 使用 allure 框架为测试用例命名
@allure.title('菜品删除')
# TODO 循环获取元素
def test_3(self, session_scope, comm):
"""菜品删除"""
# 获取售卖状态和删除按钮的元素
staus = session_scope.get_values(dish_elements['stau'])
dels = session_scope.get_values(dish_elements['delete'])
# 遍历所有菜品
for i in range(0, len(staus)):
# 点击删除按钮
dels[i].click()
time.sleep(0.5)
session_scope.click(dish_elements['yes_del'])
time.sleep(0.5)
# 根据菜品的售卖状态进行不同的断言
if staus[i].text == '停售':
assume(session_scope.get_values(dish_elements['yes']) is not None, msg='停售状态错误')
elif staus[i].text == '启售':
assume(session_scope.get_values(dish_elements['no']) is not None, msg='启售状态错误')
# 使用 allure 框架为测试用例命名
@allure.title('菜品新建')
def test_4(self, session_scope, comm):
"""菜品新建"""
# 获取当前的菜品状态列表
sum1 = session_scope.get_values(dish_elements['stau'])
# 循环创建新的菜品
for i in range(3):
# 重新进入添加页面
session_scope.click(dish_elements['new'])
assume(session_scope.get_url() == 'http://120.25.124.200/#/dish/add')
time.sleep(1)
# 填写菜品信息
inputs = session_scope.get_values(dish_elements['input'])
inputs[0].send_keys('自动设置菜品' + str(i))
inputs[2].send_keys(990 + i)
time.sleep(0.5)
inputs[1].click()
time.sleep(0.5)
item = session_scope.get_values(dish_elements['item'])
item[random.randint(0, (len(item) - 1))].click()
time.sleep(1)
# TODO 选择口味
# session_scope.click(dish_elements['addBut'])
# session_scope.click(dish_elements['box'])
# time.sleep(1)
# session_scope.click(dish_elements['add'])
# 上传图片
session_scope.input_img(dish_elements['img'], '/Users/xixi/Desktop/WechatIMG1.jpg')
time.sleep(2)
# 保存菜品
session_scope.click(dish_elements['save'])
time.sleep(1)
# 获取新的菜品状态列表并断言
sum2 = session_scope.get_values(dish_elements['stau'])
assume((len(sum2) - len(sum1)) == 0, msg='添加失败')
# 使用 allure 框架为测试用例命名
@allure.title('菜品状态更改')
# TODO 改为 ‘停售/起售卖’ 改为参数
def test_5(self, session_scope, comm):
"""菜品状态更改"""
# 获取菜品类型的元素
type1 = session_scope.get_values(dish_elements['type'])
for i in type1:
td = i.find_elements(by=By.TAG_NAME, value='td')
time.sleep(1)
# 如果当前状态为停售,则更改为启售
if td[5].text == '停售':
div = td[7].find_element(by=By.TAG_NAME, value='div')
button = div.find_elements(by=By.TAG_NAME, value='button')
time.sleep(1)
button[2].click()
session_scope.click(dish_elements['yes_del'])
time.sleep(1)
# 断言状态是否成功更改
assume(td[5].text == '启售', msg='状态修改失败')
break
运行效果:
3.8.5 test_menu_setmeal.py
import os
import random
import time
import allure
import pytest
from pytest_assume.plugin import assume
from selenium.webdriver.common.by import By
from canyin.canyi import CY
from canyin.pages.menu_order import order_elements
from canyin.pages.function_menu import f_menu_elements
from canyin.pages.menu_setmeal import setmeal_elements
# 定义一个测试类,用于套餐管理的测试
@allure.epic("餐饮后台")
@allure.feature("套餐管理")
class TestMenuSetmeal:
# 使用 pytest 的 fixture 来设置测试前的准备工作
@pytest.fixture()
def comm(self, session_scope):
session_scope.get_wait()
# 获取菜单元素并断言其不为空
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败'
# 点击第四个菜单项(假设是套餐管理)
menu[3].click()
# 定义一个测试用例,用于测试套餐查询功能
@allure.title('套餐查询')
def test_1(self, session_scope, comm):
"""套餐查询是否正常"""
# 获取当前显示的套餐名称
text1 = session_scope.get_text(setmeal_elements['name'])
# 获取查询输入框元素
a = session_scope.get_values(setmeal_elements['select'])
# 在输入框中输入套餐名称
a[0].send_keys(text1)
time.sleep(1)
# 点击查询按钮
session_scope.click(setmeal_elements['button'])
text2 = session_scope.get_text(setmeal_elements['name'])
# 断言查询前后的套餐名称一致且不为空
assume(text1 == text2, msg='名称查询不正确')
# 清除输入框内容
session_scope.get_mose(a[0])
time.sleep(1)
session_scope.click(setmeal_elements['clean'])
flag = 0
for i in range(1, 3):
# 点击分类选择器
a[i].click()
lst = session_scope.get_values(setmeal_elements['list'])
li = lst[~flag].find_elements(by=By.TAG_NAME, value='li')
for j in range(0, len(li)):
# 选择套餐类型
time.sleep(0.5)
text = li[j].text
li[j].click()
# 点击查询按钮
session_scope.click(setmeal_elements['button'])
time.sleep(1)
# 验证查询结果是否正确
type_list = session_scope.get_values(setmeal_elements['type'])
for z in type_list:
td = z.find_elements(by=By.TAG_NAME, value='td')
if text in ['商务套餐', '人气套餐']:
assume((td[3].text == text) or (td == []), '套餐不匹配')
else:
assume((td[5].text == text) or (td == []), '售卖状态不匹配')
# 清除选择
session_scope.get_mose(a[i])
session_scope.click(setmeal_elements['clean_lst'])
a[i].click()
# 定义一个测试用例,用于测试套餐修改功能
@allure.title('套餐修改')
def test_2(self, session_scope, comm):
"""套餐修改"""
# 点击修改按钮,等待页面跳转
time.sleep(1)
session_scope.click(setmeal_elements['update'])
time.sleep(1)
# 获取当前页面的 URL 并断言是否跳转到了正确的地址
url = session_scope.get_url()
assume((url[0:39] == 'http://120.25.124.200/#/setmeal/add?id='), msg='跳转正确')
session_scope.click(setmeal_elements['save'])
# 定义一个测试用例,用于测试套餐删除功能
@allure.title('套餐删除')
# TODO 循环获取元素
def test_3(self, session_scope, comm):
"""套餐删除"""
# 获取售卖状态和删除按钮的元素
staus = session_scope.get_values(setmeal_elements['stau'])
dels = session_scope.get_values(setmeal_elements['delete'])
# 遍历所有套餐
for i in range(0, len(staus)):
# 点击删除按钮
dels[i].click()
time.sleep(0.5)
session_scope.click(setmeal_elements['yes_del'])
time.sleep(0.5)
# 根据套餐的售卖状态进行不同的断言
if staus[i].text == '停售':
assume(session_scope.get_values(setmeal_elements['yes']) is not None, msg='停售状态错误')
elif staus[i].text == '启售':
assume(session_scope.get_values(setmeal_elements['no']) is not None, msg='启售状态错误')
# 定义一个测试用例,用于测试套餐新建功能
@allure.title('套餐新建')
def test_4(self, session_scope, comm):
"""套餐新建"""
# 获取当前的套餐状态列表
sum1 = session_scope.get_values(setmeal_elements['stau'])
# 循环创建新的套餐
for i in range(3):
# 重新进入添加页面
session_scope.click(setmeal_elements['new'])
assume(session_scope.get_url() == 'http://120.25.124.200/#/setmeal/add')
time.sleep(1)
# 填写套餐信息
inputs = session_scope.get_values(setmeal_elements['input'])
inputs[0].send_keys('自动设置套餐' + str(i))
inputs[2].send_keys(990 + i)
time.sleep(1)
inputs[1].click()
item = session_scope.get_values(setmeal_elements['item'])
item[random.randint(0, (len(item) - 1))].click()
time.sleep(1)
# 选择套餐
session_scope.click(setmeal_elements['addBut'])
session_scope.click(setmeal_elements['box'])
time.sleep(1)
session_scope.click(setmeal_elements['add'])
# 上传图片
session_scope.input_img(setmeal_elements['img'], '/Users/xixi/Desktop/WechatIMG1.jpg')
time.sleep(2)
# 保存并继续
session_scope.click(setmeal_elements['save'])
time.sleep(1)
# 获取新的套餐状态列表并断言
sum2 = session_scope.get_values(setmeal_elements['stau'])
assume((len(sum2) - len(sum1)) > 0, msg='添加失败')
# 定义一个测试用例,用于测试套餐状态更改功能
@allure.title('套餐状态更改')
# TODO 改为 ‘停售/起售卖’ 改为参数
def test_5(self, session_scope, comm):
"""套餐状态更改"""
# 获取套餐类型的元素
type1 = session_scope.get_values(setmeal_elements['type'])
for i in type1:
td = i.find_elements(by=By.TAG_NAME, value='td')
time.sleep(1)
# 如果当前状态为停售,则更改为启售
if td[5].text == '停售':
div = td[7].find_element(by=By.TAG_NAME, value='div')
button = div.find_elements(by=By.TAG_NAME, value='button')
time.sleep(1)
button[2].click()
session_scope.click(setmeal_elements['yes_del'])
time.sleep(1)
# 断言状态是否成功更改
assume(td[5].text == '启售', msg='状态修改失败')
break
运行效果:
3.8.6 test_menu_order.py
import os
import time
import allure
import pytest
from pytest_assume.plugin import assume
from selenium.webdriver.common.by import By
from canyin.canyi import CY
from canyin.pages.menu_order import order_elements
from canyin.pages.function_menu import f_menu_elements
# 定义一个测试类,用于订单管理的测试
@allure.epic("餐饮后台")
@allure.feature("订单管理")
class TestMenuOrder:
# 使用 pytest 的 fixture 来设置测试前的准备工作
@allure.title('订单状态切换')
@pytest.fixture()
def test_1(self, session_scope):
"""订单状态切换是否正常"""
session_scope.get_wait()
# 获取菜单元素并断言其不为空
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败'
# 点击第三个菜单项(假设是订单管理)
menu[2].click()
# 获取订单状态列表并断言其不为空
lst = session_scope.get_values(order_elements['list'])
assert lst != [], '订单状态中标签数据获取失败'
# 遍历订单状态并点击,验证当前状态的订单是否正确
for i in range(len(lst)):
lst[i].click()
time.sleep(0.5)
# 点击第一个订单状态
lst[0].click()
# 定义一个测试用例,用于测试订单号搜索功能
@allure.title('订单号搜索')
def test_2(self, session_scope, test_1):
"""订单号搜索功能是否正常"""
# 获取订单号元素
text = session_scope.get_values(order_elements['get_ord'])
# 在输入框中输入订单号
session_scope.input(order_elements['input_ord'], text[0].text)
time.sleep(1)
# 点击搜索按钮
session_scope.click(order_elements['button'])
# 获取搜索后的订单号
text1 = session_scope.get_values(order_elements['get_ord1'])
print(text1[0].text)
# 断言搜索结果是否正确
assume(((text[0].text == text1[0].text) or (text1 is None)), msg='订单号查询有误')
# 定义一个测试用例,用于测试手机号搜索功能
@allure.title('手机号搜索')
def test_3(self, session_scope, test_1):
"""手机号搜索功能是否正常"""
# 获取手机号元素
text = session_scope.get_values(order_elements['get_pho2'])
# 获取地址元素(实际数据是手机号)
text2 = session_scope.get_values(order_elements['get_pho'])
# 在输入框中输入手机号
session_scope.input(order_elements['input_pho'], text[0].text)
time.sleep(1)
# 点击搜索按钮
session_scope.click(order_elements['button'])
# 获取搜索后的手机号
text1 = session_scope.get_values(order_elements['get_pho1'])
# 断言搜索结果是否正确
assume(((text2[0].text == text1[0].text) or (text1 is None)), msg='手机号查询有误')
# 定义一个测试用例,用于测试下单时间搜索功能
@allure.title('下单时间搜索')
def test_4(self, session_scope, test_1):
"""下单时间搜索功能是否正常"""
# 获取下单时间元素
day = session_scope.get_values(order_elements['ing_value'])
time.sleep(1)
# 在开始时间输入框中输入下单时间
session_scope.input(order_elements['star_input'], day[0].text)
time.sleep(1)
# 在结束时间输入框中输入结束时间
session_scope.input(order_elements['end_input'], '2024-8-31')
# 点击搜索按钮
session_scope.click(order_elements['button'])
# 获取搜索后的下单时间
day1 = session_scope.get_values(order_elements['ing_value'])
# 断言搜索结果是否正确
assume(((day[0].text == day1[0].text) or (day1 is None)), msg='日期查询有误')
# 定义一个测试用例,用于测试查看订单操作是否正常
@allure.title('操作-查看订单')
def test_5(self, session_scope, test_1):
"""操作-查看订单是否正常"""
# 获取行元素
row = session_scope.get_values(order_elements['row'])
# 获取页面的信息
td = row[0].find_elements(by=By.TAG_NAME, value='td')
lst = []
for i in range(5):
if i == 4:
lst.append('下单时间:' + td[i].text)
else:
lst.append(td[i].text)
# 点击查看订单按钮
session_scope.click(order_elements['check'])
# 获取详细信息
lst1 = ['t_ord', 't_usr', 't_pho', 't_address', 't_time']
lst2 = []
for i in lst1:
time.sleep(1)
lst2.append(session_scope.get_text(order_elements[i]))
# 进行匹配
assume(lst2 == lst, msg='信息不匹配')
time.sleep(1)
# 点击返回按钮
session_scope.click(order_elements['return'])
# 定义一个测试用例,用于测试取消订单操作是否正常
@allure.title('操作-取消订单')
def test_6(self, session_scope, test_1):
"""操作-取消订单是否正常"""
# 点击取消订单按钮
session_scope.click(order_elements['取消'])
# 使用 JavaScript 移除输入框的 readonly 属性
js = ('document.querySelector("#app > div > div.main-container > section > div > '
'div.el-dialog__wrapper.cancelDialog > div > div.el-dialog__body > form > div > div > div > div > '
'input").removeAttribute("readonly");')
session_scope.get_js(js)
# 在输入框中输入取消原因
session_scope.input(order_elements['输入'], '订单量较多,暂时无法接单')
# 点击确定按钮
session_scope.click(order_elements['确定'])
运行效果:
3.8.7 test_menu_category.py
import os
import random
import time
import allure
import pytest
from pytest_assume.plugin import assume
from selenium.webdriver.common.by import By
from canyin.pages.function_menu import f_menu_elements
from canyin.pages.menu_category import category_elements
@allure.epic("餐饮后台")
@allure.feature("分类管理")
class TestMenuCategory:
@pytest.fixture()
def comm(self, session_scope):
session_scope.get_wait()
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败'
menu[5].click()
@allure.title('分类查询')
def test_1(self, session_scope, comm):
"""分类查询是否正常"""
text1 = session_scope.get_text(category_elements['name'])
# 点击输入框
a = session_scope.get_values(category_elements['select'])
# 输入获取到的分类名
a[0].send_keys(text1)
time.sleep(1)
# 点击查询按钮
session_scope.click(category_elements['button'])
text2 = session_scope.get_text(category_elements['name'])
# 查询前与查询后的套餐名一致且不为空,测试通过
assume(text1 == text2, msg='名称查询不正确')
# 鼠标悬停,清除
session_scope.get_mose(a[0])
time.sleep(1)
session_scope.click(category_elements['clean'])
flag = 0
# 点击分类
a[1].click()
lst = session_scope.get_values(category_elements['list'])
li = lst[~flag].find_elements(by=By.TAG_NAME, value='li')
for j in range(0, len(li)):
# 选择套餐类型
time.sleep(0.5)
text = li[j].text
li[j].click()
# 点击查询
session_scope.click(category_elements['button'])
time.sleep(1)
# 找出对应套餐下的所有套餐名称,判断是否正确
type_list = session_scope.get_values(category_elements['type'])
for z in type_list:
td = z.find_elements(by=By.TAG_NAME, value='td')
if (text == '菜品分类') or (text == '套餐分类'):
assume((td[1].text == text) or (td == []), '套餐不匹配')
# 鼠标悬停,清除,再次点击
session_scope.get_mose(a[1])
session_scope.click(category_elements['clean_lst'])
a[1].click()
运行效果:
3.8.8 test_menu_employee.py
import os
import random
import time
import allure
import pytest
from pytest_assume.plugin import assume
from selenium.webdriver.common.by import By
from canyin.pages.function_menu import f_menu_elements
from canyin.pages.menu_employee import employee_elements
@allure.epic("餐饮后台")
@allure.feature("员工管理")
class TestMenuEmployee:
@pytest.fixture()
def comm(self, session_scope):
session_scope.get_wait()
menu = session_scope.get_values(f_menu_elements['menu'])
assert menu != [], 'menu中标签数据获取失败'
menu[6].click()
@allure.title('分类查询')
def test_1(self, session_scope, comm):
"""员工名查询是否正常"""
text1 = session_scope.get_text(employee_elements['name'])
# 点击输入框
a = session_scope.get_values(employee_elements['select'])
# 输入获取到的员工名
a[0].send_keys(text1)
time.sleep(1)
# 点击查询按钮
session_scope.click(employee_elements['button'])
text2 = session_scope.get_text(employee_elements['name'])
# 查询前与查询后的员工名一致且不为空,测试通过
assume(text1 == text2, msg='名称查询不正确')
运行效果:
其实写到后面的可以看出来,大部分代码其实都差不多,写到这里其实并没有写完,但是我感觉差不多了,后面的一两个模块逻辑也差不多,所以后续会重构一部分代码,使其更加简洁,运行效果部分因为文件大小限制的问题,所以只截取了片段。
3.9 Allure测试报告(report/result)
Allure生成测试报告,然后将相关信息发送到邮箱。
3.9.1 报告结果
Allure测试报告相关数据
3.9.2 邮件发送
使用邮件模块将相关数据发送出去。
3.10 main.py
因为设计到一些隐私方面的,具体带代码就不放了,参考下图就行,打码的是文件路径。
四.小结
对于一个系统来说,功能测试和UI自动化测试是减少软件缺陷、提高软件质量的重要手段。它们各自扮演着不同的角色,并且通常在软件开发生命周期中并行进行。以下是它们的主要作用:
1. 功能测试:
- 确保功能正确性:功能测试的目的是验证软件的各个功能是否按照需求规格说明书的要求正常工作。
- 发现逻辑错误:通过测试业务逻辑,功能测试有助于发现和修复可能影响用户操作的逻辑错误。
- 回归测试:在软件开发过程中,每当代码变更后,功能测试可以用来确保新变更没有引入新的错误。
- 用户验收:功能测试结果通常作为用户验收测试的基础,确保软件满足用户的实际需求。
2. UI自动化测试:
- 提高测试效率:自动化测试可以快速重复执行,节省手动测试的时间和劳动强度。
- 持续集成:UI自动化测试可以集成到持续集成/持续部署(CI/CD)流程中,确保每次代码提交后都能自动验证UI的准确性和功能性。
- 界面一致性:UI自动化测试有助于确保用户界面在不同浏览器和设备上的一致性和响应性。
- 用户体验:通过模拟用户交互,UI自动化测试可以发现可能影响用户体验的问题,如布局错误、按钮不可点击等。
结合功能测试和UI自动化测试,可以更全面地覆盖软件测试的需求,从而减少软件发布后的用户报告问题,提高用户满意度,并减少因缺陷导致的维护成本。此外,自动化测试还可以提供更快的反馈,帮助开发团队及早发现和修复问题,从而加速软件开发周期。
对于一个系统来说,性能测试和接口测试在提高软件质量和用户体验方面扮演着至关重要的角色。它们帮助确保系统在实际运行中能够满足性能要求,并且各个组件之间能够正确地交互,感兴趣的可以查看我的这篇文章 基于餐饮后台的微信小程序测试报告(Jmeter+Charles)