pytest

基础篇

1,最简单的示例

import pytest


def test_case_01():
    print("执行test01")
    assert 1  # 断言成功


def test_case_02():
    print("执行test02")
    assert 0  # 断言失败


if __name__ == '__main__':
    pytest.main(['test_01.py'])

运行结果如下:

如图所示:

在执行完成之后先会显示test01.py,后面跟着.F

其中.代表执行成功,F代表执行失败,并且在下方会展示错误的提示


2,pytest使用步骤:

1,导入pytest

2,编写测试用例

一,无需在测试类下编写测试用例,可以直接编写测试函数
二,测试函数名必须包含test_ 开头,或者_test结尾;

3,在pytest框架下执行测试用例

在py文件内执行测试用例: pytest.main(['test_01.py'])


3,查看执行结果

一,.代表执行成功

二,F代表执行失败


4,pytest中的setup和teardown函数

首先看原有的unitest的博文中:点击进入

一,模块级别,对整个py文件作用

​ setup_module/teardown_module

二,函数级别,对测试用例作用(不在测试类中)

​ setup_function/teardown/function

三,类级别,对测试类作用

​ setup_class/teardown_class

四,方法级别,对测试类中的测试用例起作用

​ setup_method/teardown_method

下面看一下实现过程:

# -*- coding=utf-8 -*-
# @Time    : 2019/11/25 10:49 下午
# @Site    : 
# @File    : test_case_02.py
# @Software: PyCharm
# @Author 周力


import pytest

"""
函数级别的setup/teardown
"""


def setup_function():
    print("在测试用例执行前去执行")


def teardown_function():
    print("在测试用例执行后去执行")


def test_case_01():
    print("执行test01")
    assert 1  # 断言成功


def test_case_02():
    print("执行test02")
    assert 0  # 断言失败


if __name__ == '__main__':
    pytest.main(['test_case_02.py'])

打印结果如下:

# -*- coding=utf-8 -*-
# @Time    : 2019/11/25 10:49 下午
# @Site    : 
# @File    : test_case_02.py
# @Software: PyCharm
# @Author 周力


import pytest

"""
函数级别的setup/teardown
"""


def setup_function():
    print("在测试用例执行前去执行")


def teardown_function():
    print("在测试用例执行后去执行")


"""
模块级别的setup/teardown
"""


def setup_module():
    print("在模块运行前执行")


def teardown_module():
    print("在模块运行后执行")


def test_case_01():
    print("执行test01")
    assert 1  # 断言成功


def test_case_02():
    print("执行test02")
    assert 0  # 断言失败


if __name__ == '__main__':
    pytest.main(['test_case_02.py'])

# -*- coding=utf-8 -*-
# @Time    : 2019/11/25 11:14 下午
# @Site    : 
# @File    : test_case_03.py
# @Software: PyCharm
# @Author 周力

import pytest

"""
类级别和方法级别,前提是在测试类中进行使用
"""


class TestCase:  # 不需要继承
    """
    类级别的setup/teardown
    """

    def setup_class(self):
        print("在类执行前执行一次")

    def teardown_class(self):
        print("在类执行后执行一次")

    def setup_method(self):
        print("在测试方法执行前执行")

    def teardown_method(self):
        print("在测试方法执行后执行一次")

    def test_a(self):
        print("执行测试用例a")
        assert 1

    def test_b(self):
        print("执行测试用例b")
        assert 0


if __name__ == '__main__':
    pytest.main(['test_case_03.py'])

4,总结:

一,pytest测试类写法:
1,测试类开头Test,必须大写
######     2,测试类中不需要 __init__ 方法

######     3,测试类中的测试方法的编写规则和测试函数的编写规则一致

5,pytest配置文件

一,pytest的配置文件通常放在测试目录下:名称pytest.ini,命令行运行时会使用配置文件进行配置

1,配置命令行的运行参数

​ addopts = -s # 空格分割,可添加多个命令行参数,所有参数均为插件包参数

2,配置测试搜索的文件名

​ testpaths = ./scripts # 当前目录下的scripts文件夹 可以自定义

3,配置测试搜索的类名

​ python_files = test_*.py # 当前目录下所有scripts文件夹下,以test开头, 以.py为结尾的所有文件

4,配置测试搜索的函数名

​ python_classes = Test_* # 当前目录下所有scripts文件夹下,以 test_ 开头,以.py结尾的所有文件中,以Test_开头的所有类

5,配置测试搜索的测试函数名

​ python_functions = test_* # 当前目录下所有scripts文件夹下,以 test_ 开头,以.py结尾的所有文件中,以Test_开头的所有类,中的以test_开头的方法

示例如下(pytest.ini):
[pytest]

addopts = -s

testpaths = ./scripts

python_files = test_*.py

python_classes = Test*

python_functions = test*

在终端中直接输入pytest即可执行


6,pytest常用插件

一,pytest-html (生成测试报告)

​ 在配置文件中添加 addopts = -s --html=report/report.html

也就是配置文件变为:

[pytest]

addopts = -s --html=report/report.html

testpaths = scripts

python_files = test_*.py

python_classes = Test*

python_functions = test*

再次运行pytest,就会在测试目录下生成一个report的文件夹,存放着report.html文件

页面打开如图:

二,pytest-ordering(控制测试用例的执行顺序)

​ 在被执行的用例前添加 @pytest.mark.run(order=x)

示例代码如下:

# -*- coding=utf-8 -*-
# @Time    : 2019/11/27 12:10 上午
# @Site    : 
# @File    : test_ordering.py
# @Software: PyCharm
# @Author 周力


import pytest
"""
pytest 控制用例执行顺序的方法
在所需要调整用例执行顺序的函数or方法之前增加@pytest.mark.run(order=x) x表示数字
"""


class TestOrdering:
    def test_login(self):
        print("login……")
        assert 1

    def test_register(self):
        print("register……")
        assert 1

    def test_buy(self):
        print("buy(^_^)")
        assert 1

执行pytest test_ordering.py 结果如下:

可以看到,目前执行顺序是按写的顺序执行,那如果加上装饰器呢?

# -*- coding=utf-8 -*-
# @Time    : 2019/11/27 12:10 上午
# @Site    : 
# @File    : test_ordering.py
# @Software: PyCharm
# @Author 周力


import pytest
"""
pytest 控制用例执行顺序的方法
在所需要调整用例执行顺序的函数or方法之前增加@pytest.mark.run(order=x) x表示数字
数字的形式:小数;整数;等
正数:
零
负数
小数:
"""


class TestOrdering:
    @pytest.mark.run(order=3)
    def test_login(self):
        print("login……")
        assert 1

    @pytest.mark.run(order=2)
    def test_register(self):
        print("register……")
        assert 1

    @pytest.mark.run(order=1)
    def test_buy(self):
        print("buy(^_^)")
        assert 1

执行顺序恰恰相反:


使用pytest.mark.run(order=x)

执行顺序按以下排列

0>正数>没有修饰>负数>


7,Pytest 失败重试

简介:通过命令行的方式,控制失败函数的重试次数

安装pytest-rerunfailures

使用方法:

一,在配置文件中命令行参数中添加新参数

1, --reruns=2 失败重跑2次
[pytest]

addopts = -s --html=report/report.html --reruns=2

testpaths = scripts

python_files = test_*.py

python_classes = Test*

python_functions = test*

运行pytest之后完全可以看到,失败之后重跑了2次

2, 什么才算失败?

当然如果在失败重跑未达到最大次数之前成功了,就不会算做失败了


进阶篇

1,跳过测试函数

根据特定条件,不执行标志的测试函数

一,使用方法

skipif(condition, reason=None)

1,condition:跳过的条件,必传参数
2,reason:标注原因,(约定俗成必传参数)

@pytest.mark.skipif(condition, reason="XXX")

二,代码示例

# -*- coding=utf-8 -*-
# @Time    : 2019/11/27 11:47 下午
# @Site    : 
# @File    : test_skip.py
# @Software: PyCharm
# @Author 周力


import pytest
"""
跳过测试的使用方法
@pytest.mark.skipif(条件, 原因)
"""


@pytest.mark.skipif(2>1, reason="条件为真跳过")
def test_skip_01():
    print("测试跳过函数001")
    assert 2 > 1


@pytest.mark.skipif(1>2, reason="条件为假跳过")
def test_skip_01():
    print("测试跳过函数001")
    assert 1

结果如下:


2,预期失败

标记函数为失败函数

一,使用方法

xfail(condition=None, reason=None, raise=None, run=True, strict=False)

1,condition:预期失败的条件
2,reason:标注原因,(约定俗成必传参数)

二,代码示例:

# -*- coding=utf-8 -*-
# @Time    : 2019/11/28 12:00 上午
# @Site    : 
# @File    : test_xfail.py
# @Software: PyCharm
# @Author 周力


import pytest

"""
预期失败的方法
预期失败---》失败
预期失败---》成功
预期成功---》成功
预期成功---》失败
"""


@pytest.mark.xfail(1 < 2, reason="预期失败")
def test_xfail1():
    print("预期失败---》失败")
    assert 0


@pytest.mark.xfail(1 < 2, reason="预期失败")
def test_xfail2():
    print("预期失败---》成功")
    assert 1


@pytest.mark.xfail(2 > 1, reason="预期成功")
def test_xfail3():
    print("预期成功---》成功")
    assert 1


@pytest.mark.xfail(2 > 1, reason="预期成功")
def test_xfail4():
    print("预期成功---》失败")
    assert 0

结果如下:

当然我们也可以读报告

三,总结

预期失败---》失败
预期失败---》成功
预期成功---》成功
预期成功---》失败

在配置文件中添加一个参数就可以过滤掉 比如预期失败结果成功或者预期成功结果失败的情况出现

xfail_strict=true

在执行pytest后,就会把不符合预期的结果直接标记为失败

配置文件如下:

[pytest]

addopts = -s --html=report/report.html --reruns=2

testpaths = scripts

python_files = test_*.py

python_classes = Test*

python_functions = test*

xfail_strict=true

3,函数数据参数化

方便测试函数对于测试数据的获取

一,使用方法

使用parametraize(argnames, argvalues)

1,argnames,表示参数名
2,argvalues,参数值 类型是列表格式

二,使用方式

@pytest.mark.parametrize(argnames, argvalues)

1,首先看单参数的
# -*- coding=utf-8 -*-
# @Time    : 2019/11/28 10:12 下午
# @Site    : 
# @File    : test_parametrize.py
# @Software: PyCharm
# @Author 周力


import pytest
"""
语法:@parametrize(argnames, argvalues)
"""


@pytest.mark.parametrize("mobile", ["12306", "114", "110", "17621367900"])
def test_register(mobile):
    """
    单参数的测试
    用户注册使用手机号
    :return:
    """
    print(f"注册手机号为{mobile}")

运行如下:


2,多个参数示例:
# -*- coding=utf-8 -*-
# @Time    : 2019/11/28 10:12 下午
# @Site    : 
# @File    : test_parametrize.py
# @Software: PyCharm
# @Author 周力


import pytest
"""
语法:@parametrize(argnames, argvalues)
"""


@pytest.mark.parametrize("mobile", ["12306", "114", "110", "17621367900"])
@pytest.mark.parametrize("code", ["12", "23", "99"])
def test_register(mobile, code):
    """
    单参数的测试
    用户注册使用手机号
    用户注册使用验证码
    :return:
    """
    print(f"注册手机号为{mobile}")
    print(f"注册验证码为{code}")

那如果100个参数这样岂不是累死?

所以将上述参数改为"mobile, code", [(), (), ()]形式即可

4,使用pytest-fixture

优势:

firture相对于setup和teardowm来说应该有以下几点优势:

① 命名方式灵活,不局限于setup和teardown这几个命名

② conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置

③ scope="module" 可以实现多个.py跨文件共享配置,每一个文件调用一次

④ scope="session" 以实现多个.py跨文件使用一个session来完成多个用例


pytest的fixture命名不在局限于setup和teardown命名方式

所有的fixture都可以卸载一个conftest.py的文件中,方便所有测试用例的使用

一,fixture的创建
# 1,@pytest.fixture()
# 2,编写一个普通函数
# 3,创建一个fixture
@pytestfixture()
def login():
  print("我在执行登陆操作")

二,fixture的使用
# 在需要使用fixture的测试用例中,当做参数传入即可
def test_shopping(login):  # 参数就是待测试函数名
  print("测试函数")
三,使用fixture实现setup和teardown
# -*- coding=utf-8 -*-
# @Time    : 2019/11/30 8:15 下午
# @Site    : 
# @File    : test_fixture.py
# @Software: PyCharm
# @Author 周力

import pytest

"""
创建: 
@pytest.fixture()
def fixture 名称()
    代码块
使用:
def 测试用例(fixture名):
    用例步骤

编写一个fixture,并用在测试用例中


"""


# 创建一个fixture
@pytest.fixture()
def login():
    print("用户登录成功")


# 使用fixture
def test_shopping(login):
    """
    测试购物
    :param login:
    :return:
    """
    print("正在执行购物操作")


def test_browser_goods():
    """
    测试浏览商品  无需登录
    :return:
    """
    print("测试浏览商品,不需要登录")

执行结果如下:

​ test_fixture.py 用户登录成功
正在执行购物操作
.测试浏览商品,不需要登录
.

从上面可以看出,fixture默认实现了setup方法,那如果假如teardown呢?

# -*- coding=utf-8 -*-
# @Time    : 2019/11/30 8:53 下午
# @Site    : 
# @File    : test_fixture_setup_teardown.py
# @Software: PyCharm
# @Author 周力


import pytest

"""
使用yield关键字
"""


@pytest.fixture()
def login():
    print("打开浏览器,登录成功")
    yield
    print("关闭浏览器")


# 使用fixture
def test_shopping(login):
    """
    测试购物
    :param login:
    :return:
    """
    print("正在执行购物操作")


def test_ask_goods(login):
    """咨询商品需要登录"""
    print("我在咨询商品")


def test_browser_goods():
    """
    测试浏览商品  无需登录
    :return:
    """
    print("测试浏览商品,不需要登录")

pytest test_fixture_setup_teardown.py

结果如下:

test_fixture_setup_teardown.py 打开浏览器,登录成功
正在执行购物操作
关闭浏览器
.打开浏览器,登录成功
我在咨询商品
关闭浏览器
.测试浏览商品,不需要登录
.

四,使用addfinalizer(断开函数)

和yield的区别在于

① yield当用例执行完之,会执行yield后方的代码,但不能return

②addfinalizer这个实现功能和yield一样,可以return参数传给后面的用例

# -*- coding=utf-8 -*-
# @Time    : 2019/11/30 9:52 下午
# @Site    : 
# @File    : test_addfinalizer.py
# @Software: PyCharm
# @Author 周力


import pytest
from selenium import webdriver


# 创建fixture
@pytest.fixture()
def open(request):
    driver = webdriver.Chrome()
    print("打开Chrome")

    def end():
        driver.quit()

    request.addfinalizer(end)  # 终结函数---自动实现teardown
    return driver


def test_baidu(open):
    open.get("http://www.baidu.com")
    title = open.title
    print(f"百度首页标题{title}")
    assert "百度" in title


def test_sina(open):
    open.get("http://www.ina.com")
    title = open.title
    print(f"新浪首页标题{title}")
    assert "新浪" in title
五,fixture其他方法的使用
# 参数化
@pytest.fixture(params=[1, 2, 3])
def phone(request):
    """获取参数"""
    return request.param  # 返回之后如何使用呢?


def test_mobile(phone):
    print(f"电话ID{phone}")
# 参数化
@pytest.fixture(params=[1, 2, 3])  # 电话ID
def phone(request):
    """获取参数"""
    return request.param  # 返回之后如何使用呢?


# 处理测试数据
@pytest.fixture(params=[12, 34, 56, 78])  # 验证码
def logininfo(phone, request):
    return (phone, request.param)


def test_login(logininfo):
    print(f"登录数据是{logininfo}")

.登录数据是(2, 12)
.登录数据是(3, 12)
.登录数据是(1, 34)
.登录数据是(2, 34)
.登录数据是(3, 34)
.登录数据是(1, 56)
.登录数据是(2, 56)
.登录数据是(3, 56)
.登录数据是(1, 78)
.登录数据是(2, 78)
.登录数据是(3, 78)

posted on 2019-11-26 01:06  人生苦短use,what?  阅读(251)  评论(0编辑  收藏  举报