自动化测试用例
自动化测试用例¶
目标¶
PetClinic 宠物主人姓搜索接口自动化测试的目标是确保该接口在各种情况下都能够正常工作,并满足其设计和功能要求。
- 功能正确性测试
- 异常情况处理测试
- 报告生成:
- 生成详细的测试报告: 包括执行结果、通过和失败的用例、执行时间等。
- 报告分享: 将报告分享给相关团队成员,包括开发人员和测试团队。
这些目标旨在确保 PetClinic 宠物主人姓搜索接口在各种情况下都能够稳健、高效地工作,并且对异常情况能够有适当的处理。
步骤¶
以下是 PetClinic 宠物主人姓搜索接口自动化测试的一般步骤。
- 准备测试环境
- 设计测试用例
- 编写自动化测试脚本
- 执行测试用例
- 生成测试报告
通过这些步骤确保测试用例涵盖了各种情况,特别是边界条件和异常情况,以确保全面的测试覆盖。
参考示例¶
使用 pytest 测试类组织测试用例。在测试类中编写具体的测试方法,每一个测试方法是一个测试用例。
在每一条测试用例中,使用 requests 发起请求。接受接口响应内容,然后进行断言,判断用例是否通过。
import allure
import pytest
import requests
from utils.logger import logger
@allure.epic("宠物医院")
@allure.feature("搜索功能")
class TestOwnerSearch:
OWNER_URL = "https://192.168.20.30:9966/petclinic/api/owners"
@pytest.mark.P0
@allure.story("Owners搜索")
@allure.title("搜索-结果不止一个")
def test_search_more_exists(self):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:结果数量大于=2 不止一个
:return:
"""
searchkey = "Davis"
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
ownerlist = requests.request("GET", url=self.OWNER_URL, params=param).json()
logger.info("断言比较结果列表的长度大于2 len(ownerlist) >= 2")
assert len(ownerlist) >= 2
@pytest.mark.P0
@allure.story("Owners搜索")
@allure.title("搜索-结果只有一个")
def test_search_only_exists(self):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:搜索成功,得到进入 Lastname 为 Green 的宠物主人信息数据
:return:
"""
searchkey = "Green"
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
ownerlist = requests.request("GET", url=self.OWNER_URL, params=param).json()
logger.info("断言比较结果列表的长度等于1 len(ownerlist) == 1")
assert len(ownerlist) == 1
name = ownerlist[0].get("lastName")
logger.info(f"取出结果列表中的姓氏为: {name}")
logger.info("断言比较接口返回结果列表中的字典中姓氏与查询的参数一致")
assert searchkey == name
@pytest.mark.P1
@allure.story("Owners搜索")
@allure.title("搜索-模糊搜索测试")
@pytest.mark.parametrize("searchkey", ["a", "b", "A"])
def test_search_only_exists_params(self, searchkey):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:搜索成功,得到进入 Lastname 为 大写或者小写的查询条件开头的 的宠物主人信息数据
:return:
"""
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
ownerlist = requests.request("GET", url=self.OWNER_URL, params=param).json()
logger.info("断言比较结果列表的长度大于等于1 len(ownerlist) == 1")
assert len(ownerlist) >= 1
name = ownerlist[0].get("lastName")
logger.info(f"取出结果列表中的姓氏为: {name}")
logger.info("断言比较接口返回结果列表中的字典中姓氏与查询的参数一致")
assert searchkey or searchkey.upper() or searchkey.lower() in name
@pytest.mark.P1
@allure.story("Owners搜索")
@allure.title("搜索-搜索条件为空")
def test_search_all_exists(self):
"""
测试步骤:
1. 不设定lastName的值
2. 取出requests请求的结果
预期结果:搜索成功,得到全量的宠物主人信息数据
:return:
"""
ownerlist = requests.request("GET", url=self.OWNER_URL).json()
logger.info("断言比较结果列表的长度大于等于10 len(ownerlist) >= 10")
assert len(ownerlist) >= 10
@pytest.mark.P1
@allure.story("Owners搜索")
@allure.title("搜索-结果不存在")
def test_search_not_exists(self):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:搜索失败,接口响应404
:return:
"""
searchkey = "xxx"
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
code = requests.request("GET", url=self.OWNER_URL, params=param).status_code
logger.info("断言响应码为404")
assert code == 404
@pytest.mark.P2
@allure.story("Owners搜索")
@allure.title("搜索-异常输入值")
@pytest.mark.parametrize("searchkey", [
"hogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwartshogwarts",
"涨", "123", "###"])
def test_search_not_exists(self, searchkey):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:搜索失败,接口响应404
:return:
"""
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
code = requests.request("GET", url=self.OWNER_URL, params=param).status_code
logger.info("断言响应码为404")
assert code == 404
添加日志¶
编写日志工具类
import logging
import os
from logging.handlers import RotatingFileHandler
# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
log_dir_path = os.sep.join([root_path, f'/logs'])
if not os.path.isdir(log_dir_path):
os.mkdir(log_dir_path)
# 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
file_log_handler = RotatingFileHandler(os.sep.join([log_dir_path, 'log.log']), maxBytes=1024 * 1024, backupCount=10)
# 设置日志的格式
date_string = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(
'[%(asctime)s] [%(levelname)s] [%(filename)s]/[line: %(lineno)d]/[%(funcName)s] %(message)s ', date_string)
# 日志输出到控制台的句柄
stream_handler = logging.StreamHandler()
# 将日志记录器指定日志的格式
file_log_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# 为全局的日志工具对象添加日志记录器
# 绑定绑定句柄到logger对象
logger.addHandler(stream_handler)
logger.addHandler(file_log_handler)
# 设置日志输出级别
logger.setLevel(level=logging.INFO)
在测试用例中添加日志
logger.info(f"给接口设定输入内容:{searchkey}")
参数化¶
如果多个测试用例,测试步骤完全相同,只有测试数据不同,可以使用参数化的方式,在一个测试方法中完成多个测试用例的执行。
@pytest.mark.parametrize("searchkey", ["a", "b", "A"])
def test_search_only_exists(self, searchkey):
...
设置测试用例优先级¶
使用 pytest 添加标签的特性,为测试用例设置优先级。这样可以方便单独执行其中一种优先级的测试用例。
在 pytest.ini 文件中声明用例优先级
[pytest]
markers = P0
P1
P2
在测试用例中添加优先级
@pytest.mark.P0
def test_search_more_exists(self):
...
添加测试报告¶
使用 allure,在测试用例的不同位置添加测试报告信息。
@allure.epic("宠物医院")
@allure.feature("搜索功能")
class TestOwnerSearch:
OWNER_URL = "https://192.168.20.30:9966/petclinic/api/owners"
@pytest.mark.P0
@allure.story("Owners搜索")
@allure.title("搜索-结果不止一个")
def test_search_more_exists(self):
...
测试代码¶
部分用例代码如下:
@allure.epic("宠物医院")
@allure.feature("搜索功能")
class TestOwnerSearch:
OWNER_URL = "https://192.168.20.30:9966/petclinic/api/owners"
@pytest.mark.P0
@allure.story("Owners搜索")
@allure.title("搜索-结果不止一个")
def test_search_more_exists(self):
"""
测试步骤:
1. 设定lastName的值为【searchkey】
2. 使用设定的参数发起requests请求
3. 取出requests请求的结果
预期结果:结果数量大于=2 不止一个
:return:
"""
searchkey = "Davis"
logger.info(f"给接口设定输入内容:{searchkey}")
param = {"lastName": searchkey}
ownerlist = requests.request("GET", url=self.OWNER_URL, params=param).json()
logger.info("断言比较结果列表的长度大于2 len(ownerlist) >= 2")
assert len(ownerlist) >= 2