pytest测试框架02--Loxida

一、pytest结合数据驱动-yaml

1、什么是数据驱动?

数据驱动就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。简单来说,就是参数化的应用。数据量小的测试用例可以使用代码的参数化来实现数据驱动,数据量大的情况下建议大家使用一种结构化的文件(例如 yaml,json 等)来对数据进行存储,然后在测试用例中读取这些数据

2、yaml 文件介绍

1)对象:键值对的集合,用冒号 “:” 表示,在python中为字典

2)数组:一组按次序排列的值,前加 “-”,在python中为列表

3)纯量:单个的、不可再分的值,包括字符串、布尔值、整数、浮点数、Null、时间、日期
# {"languages":["PHP","Java","Python"], "book":{"Python入门":{"price": 55.5, "author": "Lily", "available": True, "repertory": 20, "date": "2018-02-17"}, "Java入门": {"price": 60, "author": "Lily", "available": "False", "repertory": "Null", "date": "2018-05-11"}}}
languages:
  - PHP
  - Java
  - Python
book:
  Python入门: # 书籍名称
    price: 55.5
    author: Lily
    available: True
    repertory: 20
    date: 2018-02-17
  Java入门:
    price: 60
    author: Lily
    available: False
    repertory: Null
    date: 2018-05-11

3、查看 yaml 文件

1)pycharm
2)txt 记事本

4、读取 yaml 文件

1)工程目录结构

    ①data 目录:存放 yaml 测试数据文件data.yaml
    ②func 目录:存放被测函数文件operation.py
    ③testcase 目录:存放测试用例文件test_add.py

2)安装:pip install pyyaml

3)方法:yaml.safe_load(f)
# yaml测试数据文件内容
# [[1,1,2],[3,6,9],[100,200,300]]
-
  - 1
  - 1
  - 2
-
  - 3
  - 6
  - 9
-
  - 100
  - 200
  - 300
# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
    result = x + y
    return result


# test_add.py 文件内容
import pytest
import yaml
from testing.func.operation import my_add


# 读取yaml文件
def get_data():
    """
    获取json数据
    :return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
    """
    # 如果yaml文件中有中文,必须要加上encoding="utf-8"
    with open('../data/data.yaml', 'r', encoding="utf-8") as f:
        data = yaml.safe_load(f)
        return data


class TestWithYAML:
    @pytest.mark.parametrize('x,y,expected', get_data())   # 测试数据调用get_data()方法读取出来的数据,需要添加数据时只需在yaml文件即可
    def test_add(self, x, y, expected):
        assert my_add(int(x), int(y)) == int(expected)

二、pytest结合数据驱动-excel

1、读取Excel文件

2、openpyxl的安装

  • 安装:pip install openpyxl
  • 导入:import openpyxl

3、openpyxl 库的操作

  • 读取工作簿
  • 读取工作表
  • 读取单元格
import openpyxl

# 获取工作簿
book = openpyxl.load_workbook('../data/params.xlsx')

# 读取工作表
sheet = book.active

# 读取单个单元格
cell_a1 = sheet['A1']
cell_a3 = sheet.cell(column=1, row=3)  # A3

# 读取多个连续单元格
cells = sheet["A1":"C3"]

# 获取单元格的值
cell_a1.value

4、工程目录结构

①data 目录:存放 excel 测试数据文件params.xlsx
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_excel.py
# 被测对象-operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
    result = x + y
    return result


# 测试用例-test_excel.py 测试用例文件内容
import openpyxl
import pytest
from testing.func.operation import my_add

def get_excel():
    # 获取工作簿
    book = openpyxl.load_workbook('../data/params.xlsx')

    # 获取活动行(非空白的)
    sheet = book.active

    # 提取数据,格式:[[1, 2, 3], [3, 6, 9], [100, 200, 300]]
    values = []
    for row in sheet:
        line = []
        for cell in row:
            line.append(cell.value)
        values.append(line)
    return values


class TestWithEXCEL:
    @pytest.mark.parametrize('x,y,expected', get_excel())
    def test_add(self, x, y, expected):
        assert my_add(int(x), int(y)) == int(expected)

三、pytest结合数据驱动-csv

1、csv文件介绍

1)csv:逗号分隔值,是 Comma-Separated Values 的缩写

2)以纯文本形式存储数字和文本

3)文件由任意数目的记录组成

4)每行记录由多个字段组成

2、csv文件的使用

1)读取数据

    ①内置函数:open()
    ②内置模块:csv
    ③方法:csv.reader(iterable)

    参数:iterable ,文件或列表对象
    返回:迭代器,每次迭代会返回一行数据。

2)工程目录结构

    ①data 目录:存放csv 测试数据文件demo.csv
    ②func 目录:存放被测函数文件operation.py
    ③testcase 目录:存放测试用例文件test_csv.py
# csv文件内容
1,1,2
3,6,9
100,200,300

# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
    result = x + y
    return result

# 测试用例文件内容
import pytest
import csv
from testing.func.operation import my_add


# 读取csv文件内容
def get_csv():
    """
    获取csv数据
    :return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
    """
    with open('../data/demo.csv', 'r') as file:
        raw = csv.reader(file)
        data = []
        for line in raw:
            data.append(line)
    return data


class TestWithCSV:
    @pytest.mark.parametrize('x,y,expected', get_csv())
    def test_add(self, x, y, expected):
        assert my_add(int(x), int(y)) == int(expected)

四、Pytest 结合数据驱动 json

1、json文件介绍

①json是JS对象,全是JavaScript Object Notation
②是一种轻量级的数据交换格式;json 结构

对象 {"key": value}
数组 [value1, value2 ...]

2、查看 json 文件

①pycharm
②txt 记事本

3、工程目录结构

①data 目录:存放json数据文件params.xlsx
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_json.py

4、读取 json 文件

①内置函数 open()
②内置库 json
③方法:json.loads()
# json数据文件
{
  "case1": [1, 1, 2],
  "case2": [3, 6, 9],
  "case3": [100, 200, 300]
}

# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
    result = x + y
    return result

# 测试用例文件内容
import pytest
import json
from testing.func.operation import my_add


# 读取json文件
def get_json():
    """
    获取json数据
    :return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
    """
    with open('../data/param.json', 'r') as f:
        data = json.loads(f.read())
        return list(data.values())


class TestWithJSON:
    @pytest.mark.parametrize('x,y,expected', [[1, 1, 2]])
    def test_add(self, x, y, expected):
        assert my_add(int(x), int(y)) == int(expected)

五、pytest测试用例生命周期管理

1、Fixture 用法

1)Fixture 特点及优势

    ①命令灵活:对于 setup,teardown,可以不起这两个名字
    ②数据共享:在 conftest.py 配置⾥写⽅法可以实现数据共享,不需要 import 导⼊。可以跨⽂件共享
    ③scope 的层次及神奇的 yield 组合相当于各种 setup 和 teardown
    ④实现参数化

2)Fixture 在自动化中的应用- 基本用法

    ①使用场景:测试⽤例执⾏时,有的⽤例需要登录才能执⾏,有些⽤例不需要登录。
    ②setup 和 teardown ⽆法满⾜。fixture 可以。默认 scope(范围)function
    ③步骤:
        a.导⼊ pytest
        b.在登录的函数上⾯加@pytest.fixture()
        c.在要使⽤的测试⽅法中传⼊(登录函数名称),就先登录
        d.不传⼊的就不登录直接执⾏测试⽅法。
import pytest
# 定义了登录的fixture,尽量避免以test开头
@pytest.fixture()
def login():
    print("完成登录操作")

def test_search():
    print("搜索")

def test_cart(login):   # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
    print("购物车")

def test_order(login):   # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
    print("下单功能")

2、Fixture 在自动化中的应用 - 作用域

【取值:范围:说明】
①function:函数级:每一个函数或方法都会调用
②class:类级别:每个测试类只运行一次
③module:模块级:每一个.py 文件调用一次
④package:包级:每一个 python 包只调用一次(暂不支持)
⑤session:会话级:每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法
# fixture的作用域
import pytest
@pytest.fixture(scope="class")
def login1():
    print("完成登录操作")

def test_search1():
    print("搜索")

def test_cart1(login1):   # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
    print("购物车")

def test_order1(login1):   # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
    print("下单功能")

class TestDemo:
    def test_case1(self, login1):
        print("case1")

    def test_case2(self, login1):
        print("case2")

3、Fixture 在自动化中的应用 - yield 关键字

1)场景:测试⽅法后销毁清除数据

2)解决:通过在 fixture 函数中加⼊ yield 关键字,yield 是调⽤第⼀次返回结果,第⼆次执⾏它下⾯的语句返回。

3)步骤:

    ①在@pytest.fixture(scope=module)。
    ②在登陆的⽅法中加 yield,之后加销毁清除的步骤
import pytest
@pytest.fixture
def login():
    # setup操作
    print("完成登录操作")
    # yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
    token = "abc"
    username = "hello"
    yield token  # 前面为setup操作,后面为teardown操作
    # teardown操作
    print("完成登出操作")

def test_search():
    print("搜索")

def test_cart(login):   # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
    print(f"token:{login}")  # 添加返回值时,token为login,拿到token值
    print("购物车")

4、Fixture 在自动化中的应用 - 数据共享

1)场景:与其他测试⼯程师合作⼀起开发时,公共的模块要在不同⽂件中,要在⼤家都访问到的地⽅。

2)解决:使⽤ conftest.py 这个⽂件进⾏数据共享,并且他可以放在不同位置起着不同的范围共享作⽤。

3)前提:conftest ⽂件名是不能换的,放在项⽬下是全局的数据共享的地⽅

4)执⾏:系统执⾏到参数 login 时先从本模块中查找是否有这个名字的变量什么的,之后在 conftest.py 中找是否有。

5)步骤:将登陆模块带@pytest.fixture 写在 conftest.py文件
# conftest.py文件
import pytest
@pytest.fixture(scope="session")
def login():
    # setup操作
    print("完成登录操作")
    # yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
    token = "abc"
    username = "hello"
    yield token  # 前面为setup操作,后面为teardown操作
    # teardown操作
    print("完成登出操作")

@pytest.fixture()
def loginDB():
    print("连接数据库")
    yield
    print("断开数据库连接")

# 新建测试用例文件
# 将登录操作放在conftest文件中,且方法需要定义为fixture,实现共享,可以直接调用
def test_cart(login):
    print("购物车")

def test_order(login):
    print("下单功能")

class TestDemo:
    def test_case1(self, login, loginDB):   # 可以添加多个fixture
        print("case1")

    def test_case2(self, login, loginDB):
        print("case2")

5、Fixture在自动化中的应用-自动应用

1)场景:

    ①不想原测试⽅法有任何改动,或全部都⾃动实现⾃动应⽤,
    ②没特例,也都不需要返回值时可以选择⾃动应⽤

2)解决:使⽤ fixture 中参数 autouse=True 实现

3)步骤:在⽅法上⾯加 @pytest.fixture(autouse=True)
# conftest.py文件
@pytest.fixture(scope="session", autouse=True)
def login():
    # setup操作
    print("完成登录操作")
    # yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
    token = "abc"
    username = "hello"
    yield token  # 前面为setup操作,后面为teardown操作
    # teardown操作
    print("完成登出操作")

# 测试用例文件
import pytest
# conftest文件中登录操作,fixture方法添加参数autouse=True,其他文件调用不需添加login方法名称,可自动生效
def test_cart():
    print("购物车")

def test_order():
    print("下单功能")

6、Fixture在自动化中的应用-参数化

1)场景:测试离不开数据,为了数据灵活,⼀般数据都是通过参数传的

2)解决:fixture 通过固定参数 request 传递

3)步骤:

    ①在 fixture 中增加@pytest.fixture(params=[1, 2, 3, ‘linda’])
    ②在⽅法参数写 request,方法体里面使用 request.param 接收参数
import pytest
@pytest.fixture(params=[["hali", 123], ["bob", 123456]])
def login(request):
    print(f"用户名和密码是:{request.param}")
    return request.param


def test_case(login):
    print(f"用户名和密码数据为:{login}"

六、pytest配置文件

1、pytest.ini是什么

①pytest.ini 是 pytest 的配置文件
②可以修改  pytest  的默认行为
③不能使用任何中文符号,包括汉字、空格、引号、冒号等等

2、pytest.ini作用

①修改用例的命名规则
②配置日志格式,比代码配置更方便
③添加标签,防止运行过程报警告错误
④指定执行目录
⑤排除搜索目录

新建一个pytest.ini文件,添加以下目录

[pytest]
;执行check_开头和 test_开头的所有的文件,后面一定要加*
python_files = check_* test_*
;执行所有的以Test和Check开头的类
python_classes = Test*  Check*
;执行所有以test_和check_开头的方法
python_functions= test_* check_*
;-vs打印日志文件,--alluredir ./results 生成报告并存放在results中,执行用例时就不用再该命令了
addopts = -vs --alluredir ./results
;设置执行的路径
testpaths = testing testcase
;忽略某些文件夹/目录
norecursedirs = result logs datas test_demo*

3、日志配置:

1)注意:windows系统 需要把中文 注释去掉。
[pytest]
;日志开关 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

七、pytest插件

1、pytest 插件分类

①外部插件:pip install 安装的插件
②本地插件:pytest自动模块发现机制,自己编写的插件(conftest.py 存放的)
③内置插件:代码内部的_pytest 目录加载

2、pytest 常用的插件

(1)pytest-ordering:控制用例的执行顺序

1)场景:对于集成测试,经常会有上下文依赖关系的测试用例。比如 10 个步骤,拆成 10 条 case,这时候能知道到底执行到哪步报错。用例默认执行顺序:自上而下执行

2)解决:可以通过 setup,teardown 和 fixture 来解决。也可以使用对应的插件。

3)安装:pip install pytest-ordering

4)用法:@pytest.mark.run(order=2)

5)注意:多个插件装饰器(>2)的时候,有可能会发生冲突
import pytest
@pytest.mark.run(order=2)
def test_case01():
    print("case01")

@pytest.mark.run(order=1)
def test_case02():
    print("case02")

(2)pytest-xdist:分布式并发执行测试用例

1)场景:假设有个报名系统,对报名总数统计,数据同时进行修改操作的时候有可能出现问题,需要模拟这个场景,需要多用户并发请求数据。

2)使用分布式并发执行测试用例。分布式插件:pytest-xdist

3)安装及运行: pip install pytest-xdist

4)注意: 用例多的时候效果明显,多进程并发执行,同时支持 allure

5)分布式执行测试用例原则

    ①用例之间是独立的,不要有依赖关系
    ②用例执行没有顺序,随机顺序都能正常执行
    ③每个用例都能重复运行,运行结果不会影响其他用例

6)运行命令

①pytest -n numcpus  :numcpus 是指几核
②pytest -n auto :不知道是几核时,auto会自动去找空闲的cpu

(3)pytest-dependency:控制用例的依赖关系

(4)pytest-rerunfailures:失败重跑

(5)pytest-assume:多重较验

(6)pytest-random-order:用例随机执行

(7)pytest-html:测试报告

3、pytest 内置插件 hook 体系

1)pytest hook介绍

    ①是个函数,在系统消息触发时被系统调用
    ②自动触发机制
    ③Hook 函数的名称是确定的
    ④pytest 有非常多的勾子函数
    ⑤使用时直接编写函数体