pytest进阶使用

1. pytest介绍

       

 

    

 

    带有init方法的类,被认为是带有特殊功能的类,就不是测试用例类

 

2. pytest配置

    要想打印log日志,必须配置pytest.ini:pytest会捕获日志,导致运行时未显示日志 

[pytest]
addopts = -sv  --log-cli-level=INFO

 

3. pytest进阶使用

3.1 命令行参数

3.1.1 运行参数

Pytest的执行有很多参数,使用帮助查看:

pytest -h

常用参数:

参数说明
-v 可以输出用例更加详细的执行信息,比如用例所在的文件及用例名称
-s 输入我们用例中的调式信息,比如print的打印信息等
-x 遇到失败的用例立即停止
-maxfail 用例失败达到一定个数时停止运行pytest --maxfail=num
-m 运行有@pytest.mark.标记名 的测试用例
-k 选择部分测试,匹配给出的表达式;eg: pytest -k “raises and not delete”(运行所有名字中包含 raises但不包含delete的测试)
–collect-only 只收集用例
pytest -sv test_demo.py 

3.1.2 Mark标签

可以对测试用例打标签,在运行测试用例的时候,可根据标签名来过滤要运行的用例

方式:在测试用例/测试类前加上:@pytest.mark.标记名

import pytest
class Test_Demo():
    @pytest.mark.demo
    def test_demo(self):
        a = 5
        b = -1
        assert a != b
        print("我的第一个测试用例")
    @pytest.mark.smoke
    def test_two(self):
        a = 2
        b = -1
        assert a != b
        print("我的第二个测试用例")

也可以在一个用例上打多个标签,多次使用@pytest.mark.标签名

@pytest.mark.demo
@pytest.mark.smoke
def test_add_02():
    b = 1 + 2
    assert 0 == b

运行:

pytest -m 标签名

注册标签消除运行warning:

  1. 创建pytest.ini文件,在文件中按如下方式添加标签名:
[pytest]
markers = demo
          smoke             

3.2 数据驱动入门

3.2.1 参数化

在测试用例的前面加上:

@pytest.mark.parametrize("参数名",列表数据)
  • 参数名:作为测试用例的参数. 字符串格式,多个参数中间用逗号隔开。
  • 列表数据:一组测试数据。list格式,多组数据用元组类型,list的每个元素都是一个元组,元组里的每个元素和按参数顺序一一对应。

3.2.2 参数化演练

import pytest
def add_function(a,b):
    return a+b
@pytest.mark.parametrize("a,b,expected",
                         [(3,5,8),
                           (-1,-2,-3),
                           (100,200,300),
                         ])
def test_add(a,b,expected):
    assert add_function(a,b) == expected

3.2.3 参数加别名

pytest执行后,会使用输入数据来作为用例的标题,但是这样不便于我们查看。可以添加ids参数指定用例说明。

import pytest
def add_function(a,b):
    return a+b
@pytest.mark.parametrize("a,b,expected",
                         [(3,5,8),
                           (-1,-2,-3),
                           (100,200,300),
                         ],ids=["int","minus","bigint"])
def test_add(a,b,expected):
    assert add_function(a,b) == expected

3.2.4 参数组合

若要获得多个参数化参数的所有组合,可以堆叠参数化装饰器

import pytest
@pytest.mark.parametrize("a", [0, 1])
@pytest.mark.parametrize("b", [2, 3])
def test_foo(a, b):
    print("测试数据组合:a->%s, b->%s" % (a, b))

运行结果

test_demo.py::test_foo[2-0] 测试数据组合:a->0, b->2 PASSED
test_demo.py::test_foo[2-1] 测试数据组合:a->1, b->2 PASSED
test_demo.py::test_foo[3-0] 测试数据组合:a->0, b->3 PASSED
test_demo.py::test_foo[3-1] 测试数据组合:a->1, b->3 PASSED

3.2.5 使用yaml文件实现用例数据参数化

注意:使用之前需要在pycharm中安装PyYAML

加载yaml文件:

yaml.safe_load(open("./data.yml"))
import pytest
import yaml
def add_function(a,b):
    return a+b
@pytest.mark.parametrize("a,b,expected",
                         yaml.safe_load(open("./data.yml"))["datas"],
                         ids=yaml.safe_load(open("./data.yml"))["myid"])
def test_add(a,b,expected):
    assert add_function(a,b) == expected

data.yml文件:

datas:
  -
    - 3
    - 5
    - 8
  -
    - -1
    - -2
    - -3
  -
    - 100
    - 200
    - 300
myid:
  - "int"
  - "minus"
  - "bigint"

读取数据抽离出来改造:

import pytest
import yaml

def get_datas():
    with open("./data.yml") as f:
        datas = yaml.safe_load(f)
        print(datas)
        add_datas=datas["datas"]
        add_ids=datas["myid"]
        return [add_datas,add_ids]

def add_function(a,b):
    return a+b
@pytest.mark.parametrize("a,b,expected",
                         get_datas()[0],
                         ids=get_datas()[1])
def test_add(a,b,expected):
    assert add_function(a,b) == expected

3.2.6 使用yaml文件实现用例步骤参数化

steps.yml

- step1
- step2
- step3

import pytest
import yaml

def step1():
    print("打开浏览器")
def step2():
    print("注册新账号")
def step3():
    print("登录新账号")
# 解析测试步骤文件
def steps(path):
    with open(path) as f:
        steps = yaml.safe_load(f)
    for step in steps:
        if "step1" in step:
            step1()
        elif "step2" in step:
            step2()
        elif "step3" in step:
            step3()

def test_foo():
    steps("./steps.yml")

https://github.com/ceshiren/HogwartsSDET12/blob/master/appium_xueqiu/page/base_page.py 1

3.3 fixture装置的使用

3.3.1 fixture使用背景

前面讲了setup、teardown可以实现在执行用例前或结束后加入一些操作,但这种都是针对整个脚本全局生效的。(注释:setup相当于这个个文件使用,每个文件里用;fixtire是对每个用例)
如果有以下场景:用例 1 需要先登录,用例 2 不需要登录,用例 3 需要先登录。很显然无法用 setup 和 teardown 来实现。fixture可以让我们自定义测试用例的前置条件,并且可以跨文件使用。

3.3.2 fixture的优势

  • 命名方式灵活,不局限于 setup 和teardown 这几个命名
  • conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到fixture
  • scope=“session” 以实现多个.py 跨文件共享

3.3.3 @pytest.fixtrue()快速体验

  1. 直接使用
import pytest
@pytest.fixture()
def myfixture():
    print("执行myfixture")

class Test_firstFile():

    def test_one(self):
        print("执行test_one")
        assert 2+3==5

    def test_two(self,myfixture):
        print("执行test_two")
        assert 1==1

    def test_three(self):
        print("执行test_three")
        assert 1+1==2
  1. 通过conftest.py文件

conftest.py这个文件名是固定的,不可以更改。
使用的时候不需要导入conftest.py,会自动寻找.

演练:创建一个conftest.py文件

import pytest
@pytest.fixture()
def myfixture():
    print("执行myfixture")

@pytest.fixture()
def connectdb():
    print("执行myfixture---connectdb")

这是使用多个fixture,在被引用时,直接传入方法名称

3.3.4 fixture中使用autouse

如果我们想要模块中每个测试函数都是用声明好的前置函数,每个测试函数都传入参数或添加装饰器,是不是很不方便?这时我们可以使用@pytest.fixture()中的参数autouse(自动使用),将其设为true(默认为false),这样每个测试函数都会自动调用该前置函数了。

语法:

@pytest.fixture(autouse="true")

3.3.5 fixture中使用作用域

语法:

@pytest.fixture(scope ="class")

scope参数有四种选择:function(测试函数级别),class(测试类级别),module(测试模块“.py”级别),session(多个文件级别)。默认是function级别。

3.3.6 fixture中使用参数

fixture函数可以参数化,在这种情况下,它们将被多次调用,每次执行一组相关测试,即依赖于这个fixture的测试,测试函数通常不需要知道它们的重新运行。

fixture函数需要传入特殊的request对象

函数里面可通过request.param访问每个参数的值

也可加ids指定参数名字

import pytest
@pytest.fixture(params=["参数1","参数2"])
def myfixture(request):
    print("执行testPytest里的前置函数,%s" % request.param)

 

3.3.7 fixture中返回参数

import pytest
@pytest.fixture(params=["参数1","参数2"])
def myfixture(request):
    return request.param
def test_print_param(myfixture):
    print("执行test_two")
    print(myfixture)
    assert 1==1

输出:

PASSED                    [ 50%]执行test_two 
参数1
PASSED                    [100%]执行test_two
参数2

3.3.8 fixture中使用yield实现teardown

fixture里面的teardown用yield来唤醒teardown的执行

import pytest
@pytest.fixture(params=["参数1","参数2"])
def connect_db(request):
    print("开始连接数据库!")
    yield request.param                  #相当于return,只是return后函数就结束了,yield返回值后,仍能继续执行后续
    print("执行teardown,关闭数据库连接")

def test_print_param(connect_db):
    print("执行test_two")
    print(connect_db)
    assert 1==1

yield 是python特有的功能,yield 前相当于tear up,yield 前相当于tear down(执行的顺序相当于:fixture-->test-->yiled-->teardown)

3.4 给pytest.ini添加更多配置

[pytest]
markers = demo
          smoke
addopts = -sv
;自定义测试文件命名规则
python_files = check*
;自定义测试类命名规则
python_classes = Test*
;自定义测试方法命名规则
python_functions = test_* check_* assert*

打印info日志配置:
[pytest]
addopts = -sv  --log-cli-level=INFO
 

4 pytest实用插件介绍

可在这里搜索使用方式:

https://pypi.org/project/pip/

##4.1. 用例失败后自动重新运行:pytest-rerunfailures
安装插件:

pip install pytest-rerunfailures

使用方法:(对整个文件

pytest test_x.py --reruns=n  #失败后重运行的次数
pytest test_x.py --reruns=n --reruns-delay m    #失败后重跑n次,每次停m秒

同时也可以在脚本中(对某一个用例)指定定义重跑的次数,这个时候在运行的时候,就无需加上 --reruns 这个参数

@pytest.mark.flaky(reruns=6, reruns_delay=2)
    def test_example(self):
        print(3)
        assert random.choice([True, False])

4.2 多重校验:pytest-assume

pytest中可以用python的assert断言,也可以写多个断言,但一个失败,后面的断言将不再执行。

使用pytest-assume就会执行所有的断言,如果有失败,依然会执行后面的断言(应用于自动化测试时校验多个值)

安装插件:

pip install pytest-assume

使用方法:

def test_simple_assume(x, y):
    pytest.assume(x == y)
    pytest.assume(True)
    pytest.assume(False)

4.3 分布式并发执行:pytest-xdist

pytest-xdist的出现就是为了让自动化测试用例可以分布式执行,从而节省自动化测试时间

分布式执行用例的设计原则:

  1. 用例之间是独立的,用例之间没有依赖关系,用例可以完全独立运行【独立运行】
  2. 用例执行没有顺序,随机顺序都能正常执行【随机执行】
  3. 每个用例都能重复运行,运行结果不会影响其他用例【不影响其他用例】

安装插件:

pip install pytest-xdist

使用方法:

多cpu并行执行用例,直接加个-n参数即可,后面num参数就是并行数量,比如num设置为3

pytest -n 3

4.4 控制用例的执行顺序:pytest-ordering

安装插件:

pip install pytest-ordering

使用方法:

import pytest

@pytest.mark.run(order=2)
def test_foo():
    assert True

@pytest.mark.run(order=1)
def test_bar():
    assert True

注意:尽量不要让测试用例有顺序,尽量不要让测试用例有依赖!

4.5 hook(钩子)函数定制和扩展插件【了解】

官网:https://docs.pytest.org/en/latest/_modules/_pytest/hookspec.html

pytest_collection_modifyitems

Pytest在收集完所有测试用例后调用该钩子方法。我们可以定制化功能实现:

  1. 自定义用例执行顺序
  2. 解决编码问题(中文测试用例名称)
  3. 自动添加标签

例如:
conftest.py里面:

添加:


def pytest_collection_modifyitems(session, config, items):
    print(type(items))                #items是一个列表
    items.reverse()
    for item in items:
        item.name = item.name.encode('utf-8').decode('unicode-escape')
        item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')

        if "add" in item._nodeid:
            item.add_marker(pytest.mark.add)
        if "div" in item._nodeid:
            item.add_marker(pytest.mark.div)


5 Allure

安装:https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/2.13.7/

  • mac直接brew install allure安装
  • windows直接下载zip包,解压后把bin路径配置到path里面,使用allure.bat
  • 命令行输入allure查看安装是否成功。

pytest里面安装allure-pytest
使用allure:

 pytest --alluredir ./report

生成的result记录详细结果,中间结果。最终结果:

allure serve ./report

起了一个服务,后续会进阶学习。
暂时服务,长期服务用jenkins,或者在本地搭建网站服务:Django。
python mange.py runserver (http://127.0.0.1:8000)

生成html文件:

allure generate ./report

生成html文件,open in chrome

posted @ 2020-12-13 15:39  方园FPP  阅读(902)  评论(0编辑  收藏  举报