# conftest.py
import pytest
from .pythonCode.calculator import Calculator
@pytest.fixture(scope="class")
def getCalcu():
print("开始测试")
calcu = Calculator()
yield calcu
print("结束测试")
@pytest.fixture(autouse=True)
def setup_teardown_methond():
logging.info("开始计算")
print("开始计算")
yield
logging.info("开始计算")
print("结束计算")
# test_calcu测试文件
@allure.feature("计算器")
class TestCalculator:
add_p0_datas = readYaml("P0")[0]
add_p0_ids = readYaml("P0")[1]
add_P1_1_datas = readYaml("P1_1")[0]
add_P1_1_ids = readYaml("P1_1")[1]
add_P1_2_datas = readYaml("P1_2")[0]
add_P1_2_ids = readYaml("P1_2")[1]
@pytest.mark.parametrize("first, second, excepted", add_p0_datas, ids=add_p0_ids)
@pytest.mark.P0
@allure.title("P0级别的计算器测试用例")
@pytest.mark.run(order=3)
def test_add1(self, first, second, excepted, getCalcu):
assert getCalcu.add(first, second) == excepted
@pytest.mark.parametrize("first, second, excepted", add_P1_1_datas, ids=add_P1_1_ids)
@pytest.mark.P1_1
@allure.title("P1_1级别的计算器测试用例")
@pytest.mark.run(order=2)
def test_add2(self, first, second, excepted, getCalcu):
assert getCalcu.add(first, second) == excepted
@pytest.mark.parametrize("first, second, excepted", add_P1_2_datas, ids=add_P1_2_ids)
@pytest.mark.P1_2
@allure.title("P1_2级别的计算器测试用例")
@pytest.mark.run(order=1)
def test_add3(self, first, second, excepted, getCalcu):
with pytest.raises(eval(excepted)):
result = getCalcu.add(first, second)
@pytest.fixture(scope="class", autouse=True)
def fixture_class(self):
print("实例化calculator对象")
self.calc = Calculator()
yield
print("结束测试")
@pytest.fixture(autouse=True)
def start(self):
print("开始计算")
yield
print("结束计算")
conftest.py
import os
import time
import logging
import pytest
def pytest_collection_modifyitems(items):
"""
测试用例收集完成时,将收集到的item的name和nodeid的中文显示
:return:
"""
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
@pytest.fixture(scope="class", autouse=True)
def get_calc():
yield Calculator()
@pytest.fixture(scope="class", autouse=True)
def setup_function():
logging.info("开始计算")
yield
logging.info("结束计算")
@pytest.fixture(scope="module", autouse=True)
def setup_function():
yield
logging.info("测试结束")
# 日志文件名
def pytest_configure(config):
now = time.strftime('%Y%m%d_%H%M%S') # 以日期时间做报告文件名,'%Y%m%d_%H%M%S'
logfile_name = f'{now}.log' # 日志文件名
config.option.log_file = os.path.join(config.rootdir, 'log', logfile_name)
pytest.ini
[pytest]
log_cli = true
log_cli_level = info
addopts = --capture=no -vs
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file = ./log/test.log
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
markers=P0
P1
Author: Jakie King
#!/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
from hogwarts_py23_testcode.pytest01.pythoncode.Calculator import Calculator
import pytest
class TestCalculator:
@pytest.fixture(scope="class")
def setup_cal(self):
cal = Calculator()
return cal
@pytest.fixture(scope="function", autouse=True)
def counting(self):
print("开始计算-----")
yield
print("结束计算-------")
@pytest.fixture(scope="class", autouse=True)
def ending_test(self):
yield
print("测试结束-------")
@pytest.mark.case_p0
@pytest.mark.parametrize('a,b,expected', [(1,1,2), (-0.01,0.02,0.01), (10,0.02,10.02), (-99,98.99, -0.01), (99,98.99, 197.99)])
def test_add_p0_01(self, setup_cal, a, b, expected):
result = setup_cal.add(a, b)
print("result---", result)
# 实际结果 与 预期结果 对比
assert result == expected
import allure
import pytest
import yaml,logging
from pythoncode.calculator import Calculator
@pytest.fixture(scope='function',autouse=True)
def calculate():
print('开始计算')
yield
print('结束计算')
@pytest.fixture(scope='function')
def calc():
return Calculator
def get_logger():
logger = logging.getLogger()
logger.setLevel(logging.INFO)
file_hander=logging.FileHandler('my.log', encoding='utf-8')
streamHandler=logging.StreamHandler()
fmt = logging.Formatter('%(asctime)s [%(levelname)s %(message)s (%(filename)s:%(lineno)s)',
datefmt='%m/%d/%Y_%I:%M:%S %p')
file_hander.setFormatter(fmt)
streamHandler.setFormatter(fmt)
logger.addHandler(file_hander)
logger.addHandler(streamHandler)
return logger
@allure.feature('测试加算器功能的加法运算')
class TestCalculator():
def setup(self):
self.logger=get_logger()
def teardown(self):
pass
def teardown_class(self):
print('结束测试')
@allure.story('测试正常的加法运算')
# @pytest.mark.run(order=2)
@pytest.mark.p0_data
@pytest.mark.parametrize('x,y,excepted',yaml.safe_load(open('../data/data.yaml',encoding='utf-8'))['data'])
def test_add0(self,x,y,excepted,calc):
# 测试相加方法
# calc = Calculator()
with allure.step('先进行加法运算'):
result = calc().add(x,y)
allure.attach.file('./1.png',name='测试结果的图片',attachment_type=allure.attachment_type.PNG)
self.logger.info(f'结果是{result}')
# print(result)
with allure.step('实际结果 对比 预期结果'):
# 实际结果 对比 预期结果
assert result ==excepted
# @pytest.mark.run(order=1)
@allure.story('测试异常的加法运算')
@pytest.mark.p0_data1
@pytest.mark.parametrize('x,y,exceptederror', yaml.safe_load(open('../data/data.yaml', encoding='utf-8'))['data1'])
def test_add1(self, x, y, exceptederror,calc):
# 测试相加方法
with allure.step('先进行设置异常的捕获'):
with pytest.raises(eval(exceptederror))as e:
# calc = Calculator()
result =calc().add(x, y)
self.logger.info(f'捕获的异常类型是{e.type}')
# print(e.type)
with allure.step('实际异常类型结果 对比 预期异常类型结果'):
# 实际结果 对比 预期结果
assert e.type == eval(exceptederror)
#conftest.py
@pytest.fixture(autouse=True)
def fixture_calc():
print("开始计算")
yield
print("结束计算")
@pytest.fixture(scope="class", autouse=True)
def fixture_test():
testcalc = TestCalc()
yield testcalc
print("结束测试")
#test_calc.py
@pytest.mark.run(order=2)
@pytest.mark.P0
@pytest.mark.parametrize('value1, value2, result', CaseData().useData('P0')[0], ids=CaseData().useData('P0')[1])
def test_P0(self, value1, value2, result):
try:
logging.info(f"加法{value1}+{value2}")
assert self.calc.add(value1, value2) == result
except Exception as e:
print(f"异常信息为:{e}")
日志
;日志开关 true false
log_cli = true
;日志级别
log_cli_level = info
;打印详细日志,相当于命令行加 -vs
addopts = --capture=no
;日志格式
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
;日志时间格式
log_cli_date_format = %Y-%m-%d %H:%M:%S
;日志文件位置
log_file = ./log/test.log
;日志文件等级
log_file_level = info
;日志文件格式
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
;日志文件日期格式
log_file_date_format = %Y-%m-%d %H:%M:%S
建议:
- 建议fixture名字,不要与系统名字过于相似
- 需要添加日志
conftest.py
@pytest.fixture()
def calc():
print("实例化calculator对象")
calc = Calculator()
print("【计算开始】")
yield calc
print("【计算结束】")
@pytest.fixture()
def logout():
log_path = os.path.join(os.path.dirname(os.getcwd()), "log")
if not os.path.exists(log_path):
os.makedirs(log_path)
log_path = os.path.join(log_path, "test_log.txt")
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG)
handler = logging.FileHandler(log_path)
logger.addHandler(handler)
return logger
test_calc.py
class TestCalculator:
def teardown_class(self):
print("结束测试")
# 冒烟测试用例
@pytest.mark.P0
# 需要至少两个参数,第一参数是字符串- 里面存放要传递的参数,以逗号隔开
# 第二参数,就是我们要传递的数据序列(可以列表,可以元组),每个序列里存放一组数据
@pytest.mark.parametrize('a,b, expect',
[[1, 1, 2], [-0.01, 0.02, 0.01], [10, 0.02, 10.02]],
ids=['整型', '浮点型', '整型+浮点型'])
def test_add0(self, calc, logout, a, b, expect):
result = calc.add(a, b)
logout.info(f"结果是{result}")
assert result == expect
pytest.ini
[pytest]
addopts = -vs
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
test_calc.py
import pytest
import time
import logging
import yaml
import allure
import Calculator
@pytest.fixture(autouse=True, scope="session")
def init_project(request):
# 创建日志文件名称
now = time.strftime("%Y-%m-%d %H-%M-%S")
log_name = 'log/' + now + '.log'
request.config.pluginmanager.get_plugin("logging-plugin").set_log_path(log_name)
logging.info("project init")
yield Calculator()
logging.info("project end")
@pytest.fixture(autouse=True, scope="function")
def init_testcase():
logging.info("testcase init")
yield
logging.info("testcase end")
def get_datas(level):
# safe_load: 把yaml 格式 转成python对象
# safe_dump: 把python对象 转成yaml格式
with open("./datas.yml", encoding="utf-8") as f:
result = yaml.safe_load(f)
add_datas = result.get("add").get(level).get('datas')
add_ids = result.get("add").get(level).get('ids')
print(f"{level} 级别的datas {add_datas}")
print(f"{level} 级别的ids {add_ids}")
# P0 级别的datas [[1, 1, 2], [-0.01, 0.02, 0.01], [10, 0.02, 10.02]]
# P0 级别的ids ['2个整数', '2个浮点数', '整数+浮点数']
return [add_datas, add_ids]
class TestCalculator:
add_P0_datas = get_datas("P0")[0]
add_P0_ids = get_datas("P0")[1]
add_P1_1_datas = get_datas("P1_1")[0]
add_P1_1_ids = get_datas("P1_1")[1]
add_P1_2_datas = get_datas("P1_2")[0]
add_P1_2_ids = get_datas("P1_2")[1]
add_P2_datas = get_datas("P2")[0]
add_P2_ids = get_datas("P2")[1]
@allure.feature("加法")
@allure.story("正向测试")
@pytest.mark.P0
@pytest.mark.parametrize("num1, num2, expect", add_P0_datas, ids=add_P0_ids)
def test_add_1(self, num1, num2, expect, init_project):
result = init_project.add(num1, num2)
logging.info("testcase: {} + {}".format(num1, num2))
logging.info("expect: {}".format(expect))
logging.info("result: {}".format(result))
assert result == expect
@allure.feature("加法")
@allure.story("边界值测试")
@pytest.mark.P0
@pytest.mark.parametrize("num1, num2, expect", add_P1_1_datas, ids=add_P1_1_ids)
def test_add3(self, num1, num2, expect, init_project):
result = init_project.add(num1, num2)
logging.info("testcase: {} + {}".format(num1, num2))
logging.info("expect: {}".format(expect))
logging.info("result: {}".format(result))
assert result == expect
@pytest.mark.run(order=3)
@pytest.mark.P0
# 需要至少两个参数,第一参数是字符串- 里面存放要传递的参数,以逗号隔开
# 第二参数,就是我们要传递的数据序列(可以列表,可以元组),每个序列里存放一组数据
@pytest.mark.parametrize('a,b, expect',
add_P0_datas,
ids=add_P0_ids)
@allure.severity(severity_level=allure.severity_level.BLOCKER)
def test_add0(self, a, b, expect):
with allure.step("1"):
time.sleep(1)
# 测试相加方法
with allure.step("2"):
result = self.calc.add(a, b)
with allure.step("3"):
logging.info(result)
# 实际结果 对比 预期结果
allure.attach('./test.png', name="测试", attachment_type=allure.attachment_type.PNG, extension='.png')
assert result == expect
[pytest]
addopts = -vs -n auto --alluredir=./result
@allure.feature("计算器加法测试")
class TestCalculator:
add_P0_datas = get_datas("P0")[0]
add_P0_ids = get_datas("P0")[1]
add_P1_1_datas = get_datas("P1_1")[0]
add_P1_1_ids = get_datas("P1_1")[1]
add_P1_2_datas = get_datas("P1_2")[0]
add_P1_2_ids = get_datas("P1_2")[1]
add_P2_datas = get_datas("P2")[0]
add_P2_ids = get_datas("P2")[1]
# 冒烟测试用例
@allure.story("计算器加法冒烟测试用例")
@pytest.mark.run(order=3)
@pytest.mark.P0
# 需要至少两个参数,第一参数是字符串- 里面存放要传递的参数,以逗号隔开
# 第二参数,就是我们要传递的数据序列(可以列表,可以元组),每个序列里存放一组数据
@pytest.mark.parametrize('a,b, expect',
add_P0_datas,
ids=add_P0_ids)
def test_add0(self, a, b, expect, get_calc):
# 测试相加方法
with allure.step("读取测试数据,执行测试结果"):
result = get_calc.add(a, b)
logging.info(result)
allure.attach.file(source="../file/1.jpg", attachment_type=allure.attachment_type.JPG)
# 实际结果 对比 预期结果
with allure.step("断言实际结果与预期结果"):
assert result == expect
@pytest.mark.run(order=3)
@pytest.mark.P0
# 需要至少两个参数,第一参数是字符串- 里面存放要传递的参数,以逗号隔开
# 第二参数,就是我们要传递的数据序列(可以列表,可以元组),每个序列里存放一组数据
@pytest.mark.parametrize('a,b, expect',
add_P0_datas,
ids=add_P0_ids)
@allure.story('这是第一个')
def test_add0(self, a, b, expect,bigan):
# 测试相加方法
with allure.step('测试步骤一'):
result = self.calc.add(a, b)
with allure.step('第二步截图'):
allure.attach.file(r'C:\Users\Administrator\Pictures\728220.png',name='截图',attachment_type=allure.attachment_type.PNG)
sleep(1)
with allure.step('第三步打印日志加输出'):
logging.info(f'输入数据:{a},{b},预期结果:{result}')
print(result)
# 实际结果 对比 预期结果
assert result == expect
命令:
pytest -n auto .\test_calc_params.py --alluredir .\result\ --clean-alluredir
allure serve .\result\
练习二
运行命令
pytest -n 4 --alluredir=./result
allure generate ./result -o ./report
allure报告
pytest.ini
[pytest]
addopts = -vs
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
test_calc.py
"""
__author__ = '霍格沃兹测试开发学社'
__desc__ = '更多测试开发技术探讨,请访问:https://ceshiren.com/t/topic/15860'
"""
import pytest
import time
import logging
import yaml
import allure
from ..pythoncode.calculator import Calculator
@pytest.fixture(autouse=True, scope="session")
def init_project(request):
# 创建日志文件名称
now = time.strftime("%Y-%m-%d %H-%M-%S")
log_name = 'log/' + now + '.log'
request.config.pluginmanager.get_plugin("logging-plugin").set_log_path(log_name)
logging.info("project init")
yield Calculator()
logging.info("project end")
@pytest.fixture(autouse=True, scope="function")
def init_testcase():
logging.info("testcase init")
yield
logging.info("testcase end")
def get_datas(level):
# safe_load: 把yaml 格式 转成python对象
# safe_dump: 把python对象 转成yaml格式
with open("resource/datas.yml", encoding="utf-8") as f:
result = yaml.safe_load(f)
add_datas = result.get("add").get(level).get('datas')
add_ids = result.get("add").get(level).get('ids')
print(f"{level} 级别的datas {add_datas}")
print(f"{level} 级别的ids {add_ids}")
# P0 级别的datas [[1, 1, 2], [-0.01, 0.02, 0.01], [10, 0.02, 10.02]]
# P0 级别的ids ['2个整数', '2个浮点数', '整数+浮点数']
return [add_datas, add_ids]
class TestCalculator:
add_P0_datas = get_datas("P0")[0]
add_P0_ids = get_datas("P0")[1]
add_P1_1_datas = get_datas("P1_1")[0]
add_P1_1_ids = get_datas("P1_1")[1]
add_P1_2_datas = get_datas("P1_2")[0]
add_P1_2_ids = get_datas("P1_2")[1]
add_P2_datas = get_datas("P2")[0]
add_P2_ids = get_datas("P2")[1]
@allure.feature("加法")
@allure.story("正向测试")
@pytest.mark.P0
@pytest.mark.parametrize("num1, num2, expect", add_P0_datas, ids=add_P0_ids)
def test_add_1(self, num1, num2, expect, init_project):
time.sleep(1)
allure.step('调用加法计算')
result = init_project.add(num1, num2)
logging.info("testcase: {} + {}".format(num1, num2))
logging.info("expect: {}".format(expect))
logging.info("result: {}".format(result))
allure.attach.file("./resource/photo.png", name="图片", attachment_type=allure.attachment_type.PNG)
assert result == expect
@allure.feature("加法")
@allure.story("边界值测试")
@pytest.mark.P0
@pytest.mark.parametrize("num1, num2, expect", add_P1_1_datas, ids=add_P1_1_ids)
def test_add3(self, num1, num2, expect, init_project):
time.sleep(1)
allure.step('调用加法计算')
result = init_project.add(num1, num2)
logging.info("testcase: {} + {}".format(num1, num2))
logging.info("expect: {}".format(expect))
logging.info("result: {}".format(result))
allure.attach.file("./resource/photo.png", name="图片", attachment_type=allure.attachment_type.PNG)
assert result == expect
实战一 使用 Fixture
- 使用 fixture 提供 calc 对象
- 使用 fixture 实现:用例执行之前打印【开始计算】,之后【结束计算】
- 当前模块所有用例执行完成之后,打印【测试结束】
- 每条用例添加测试日志,并将日志打印输出到 logs/ <日期_时间>.log 文件中
思路:
- 定义fixture方法,这个可以随自己喜好
这里定义两个:
【1】完成:* 使用 fixture 提供 calc 对象、* 当前模块所有用例执行完成之后,打印【测试结束】
【2】完成:* 使用 fixture 实现:用例执行之前打印【开始计算】,之后【结束计算】
conftest.py
@pytest.fixture(scope="class")
def make_cal():
cal = Calculator()
yield cal
print("测试结束")
@pytest.fixture(autouse=True) #因为每个用例都要打印,所以设置为autouse
def print_end():
print("开始计算")
print("结束计算")
2.添加日志
pytest支持在ini文件更改log设置
pytest.ini
下面配置了命令行和文件输出的格式和等级和时间显示格式
log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_file_level = info
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format = %Y-%m-%d %H:%M:%S
如果想要自定义log文件名称,需添加下面这个fixture
conftest.py
@pytest.fixture(scope="session", autouse=True)
def manage_logs(request):
"""Set log file name same as test name"""
now = time.strftime("%Y-%m-%d %H-%M-%S")
log_name = './log/' + now + '.logs'
request.config.pluginmanager.get_plugin("logging-plugin") \
.set_log_path(log_name)
实战二
- 假设每条用例执行需要 1 秒,加速执行用例(速度提升一倍以上)
思路:
加速执行可以使用 xdist
pip install pytest-xdist
命令行输入:
pytest -n auto test_cal.py
//意思为 自动选择核数加快运行py 文件,也可以auto改为自己的CPU核数
- 生成测试报告(添加用例分类,添加测试步骤,添加图像<本地任意图片>)
思路:使用allure来实现
摘取一段代码展示:
@pytest.mark.P1
# @pytest.mark.run(order=2)
@allure.story("无效边界值相加") #添加用例分类
@pytest.mark.parametrize("x,y,expected",test_func.get_data("P1-2")[0],
ids=test_func.get_data("P1-2")[1])
def test_Ca_add_P1_2(self,make_cal,x,y,expected):
sleep(1)
try:
logging.info(f"参数:{x,y},正在运行P1-2 case")
with allure.step("参数相加"): #添加用例步骤
result = make_cal.add(x,y)
with allure.step("判断是否和期望结果一致"): #添加用例步骤
assert result == expected
allure.attach("E:\Life\20200818204615.jpg", #使用allure.attach附上截图
name="截图",
attachment_type = allure.attachment_type.JPG,
extension=".jpg") #添加用例截图
except TypeError as e:
print(f"输入有问题,输入值为{x,y},抛出异常:{e}")