pytest框架实践

安装

pip install pytest -i https://mirrors.aliyun.com/pypi/simple/

测试用例

  • 测试用例要遵从test_xx.py或者xx_test.py的命名规则
  • 类名也要遵从上述命名规则 - TestListRevers

运行方式

通过标记表达式执行
pytest -m demo
这条命令会执行被装饰器@pytest.mark.demo装饰的所有测试用例

生成html报告:
pytest -m demo --html=Report/report.html

生成xml报告:
pytest -m demo --junitxml=Report/report.xml

运行指定模块:
pytest -m demo --html=Report/report.html TestCases/test_pytest.py

运行指定测试目录
pytest -m demo --html=Report/report.html TestCases/

通过节点id来运行:
pytest TestCases/test_pytest.py::TestDemo::test_demo01

通过关键字表达式过滤执行
pytest -k "MyClass and not method"
这条命令会匹配文件名、类名、方法名匹配表达式的用例

pytest.main(["-m","demo","--html=Report/report.html"])

获取用例执行性能数据
获取最慢的10个用例的执行耗时
pytest --durations=10

mark测试用例

import pytest


class TestListRevers:
    def test_list_revers(self):
        params = [1, 2, 3, 4]
        result = ListRevers(params).list_revers()
        print(result)
        assert result is [4, 3, 2, 1]

    @pytest.mark.others
    def test_list_others(self):
        assert 1 == "1"

测试用例参数化

from buisiness.list_reverse import ListRevers
import pytest


class TestListRevers:
    def test_list_revers(self):
        params = [1, 2, 3, 4]
        result = ListRevers(params).list_revers()
        print(result)
        assert result is [4, 3, 2, 1]

    @pytest.mark.others
    def test_list_others(self):
        assert 1 == "1"

    @pytest.fixture
    def input_value_inner(self):
        input = 39
        return input

    def test_divisible_by_3(self, input_value_inner):
        assert input_value_inner % 3 == 0

    def test_divisible_by_6(self, input_value):
        assert input_value % 6 == 0

    # 测试用例参数化在这里,使用的思路和unittest框架基本一样
    @pytest.mark.parametrize("num,output", [(1, 11), (2, 22), (3, 35), (4, 44)])
    def test_multiplication_11(self, num, output):
        assert 11 * num == output

测试用例setup和teardown

by class

from pytestDemo.business.calulator import Caculator
import pytest

# 注意在这里只执行了setup_class的而且其他都没有执行
# pytest的断言使用的是assert
class TestExample:
    # 注意pytest不能有 __init__方法
    # def __init__(self):
    #     print("初始化")

    # 定义一个全局变量
    # 开始于每个模块的环境准备
    def setup_module(self):
        print("\n===这是setup_module====")


    def teardown_module(self):
        print("\n===这是teardown_module====")

    def setup(self):
        print("\n===setup====")


    def teardown(self):
        print("\n===teardown====")

    # 开始于买个function,类中不能有function的东西
    # def setup_function(self):
    #     print("\n===setup_function====")
    #     # global cal
    #     self.cal = Caculator()
    #
    # def teardown_function(self):
    #     print("\n===teardown_function====")

    def setup_class(self):
        print("\n===setup_class====")
        # global cal
        self.cal = Caculator()

    def teardown_class(self):
        print("\n===teardown_class====")

    def test_div_01(self):
        print("TC01")
        actual = self.cal.divide(10, 5)
        expect = 2
        assert expect == actual

    def test_div_02(self):
        print("TC02")
        actual = self.cal.divide(10, 2)
        expect = 5
        assert expect == actual

# unittest 框架
import unittest
import pytest

class TestMyCases(unittest.testcase):

    def setup(self):
        print("每个个测试用例前先执行")


    def teardown(self):
        print("每个测试用例后先执行")


    def test_01(self):
        print("this is tcs")

# 推荐使用fixture
@pytest.fixture(name="tcs",scope="function")
def tcs_fixture():
    print("pytest执行")

by function

from pytestDemo.business.calulator import Caculator
import pytest

def setup():
    print("\n===setup====")

def teardown():
    print("\n===teardown====")

def setup_class():
    print("\n===setup_class====")
    # global cal

def teardown_class():
    print("\n===teardown_class====")

def setup_function():
    print("\n===setup_function====")
    # global cal

def teardown_function():
    print("\n===teardown_function====")

def test_div_01():
    print("TC01")
    actual = Caculator().divide(10, 5)
    expect = 2
    assert expect == actual

def test_div_02():
    print("TC02")
    actual = Caculator().divide(10, 2)
    expect = 5
    assert expect == actual

by fixture and 指定用法

from pytestDemo.business.calulator import Caculator
import pytest

#(scope="function",autouse=True)
@pytest.fixture
def setup_teardown():
    print("\n===setup====by fixture")
    yield
    print("\n====teardown====by fixture")


def test_div_01(setup_teardown):
    print("TC01")
    actual = Caculator().divide(10, 5)
    expect = 2
    assert expect == actual

def test_div_02(setup_teardown):
    print("TC02")
    actual = Caculator().divide(10, 2)
    expect = 5
    assert expect == actual

by fixture and autouse

from pytestDemo.business.calulator import Caculator
import pytest

#(scope="function",autouse=True)
@pytest.fixture(scope="function",autouse=True)
def setup_teardown():
    print("\n===setup====by fixture")
    yield
    print("\n====teardown====by fixture")


def test_div_01():
    print("TC01")
    actual = Caculator().divide(10, 5)
    expect = 2
    assert expect == actual

def test_div_02():
    print("TC02")
    actual = Caculator().divide(10, 2)
    expect = 5
    assert expect == actual

fixture至于conftest.py文件作为setup和teardown使用

conftest.py文件内容

import pytest


@pytest.fixture
def input_value():
    input = 39
    return input


@pytest.fixture
def my_setup():
    print("执行setup,这个放在conftest.py")


@pytest.fixture(scope="function", autouse=True)
def my_setup2():
    print("执行setup,这个放在conftest.py")
    yield
    print("执行teardown,这个放在conftest.py")

测试用例部分

from buisiness.list_reverse import ListRevers
import pytest


class TestListRevers:
     @pytest.mark.parametrize("num,output", [(1, 11), (2, 22), (3, 35), (4, 44)])
    def test_multiplication_11(self, my_setup, num, output):
        assert 11 * num == output

    @pytest.mark.parametrize("num,output", [(1, 11), (2, 22), (3, 35), (4, 44)])
    def test_multiplication_12(self, num, output):
        assert 11 * num == output

fixture scope & order

fixture 都是在 test 第一次调用时创建,根据 scope 的不同有不同的运行和销毁方式
function 每个函数运行一次,函数结束时销毁
class 每个类运行一次,类结束时销毁
module 每个模块运行一次,模块结束时销毁
package 每个包运行一次,包结束时销毁
session 每个会话运行一次,会话结束时销毁
fixture 的顺序优先按 scope 从大到小,session > package > module > class > function。
如果 scope 相同,就按 test 调用先后顺序,以及 fixture 之间的依赖关系。
autouse 的 fixture 会优先于相同 scope 的其他 fixture。

执行命令

命令行

pytest file.py -v
pytest -k <substring> -v
pytest -m others -v # 执行测试用例中标记为others的测试用例

自动测试测试框架执行层 

# from commandline use
# pytest.py -s pyfile.py
import sys
import pytest
import os
# print(os.path.abspath(os.path.dirname(__file__)))


class PytestRunner:
    def __init__(self):
        ROOT_DIR3 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        print(ROOT_DIR3)

    def pytest_runner(self):
        # -s 显示打印print信息
        # pytest.main(["../pytest_example/test_onefunction.py","-s","--verbose"])
        # pytest.main(["../pytest_example/pytest_traditonbyFunction.py", "-s", "--verbose"])
        # pytest.main(["../pytest_example/pytest_traditionByClass.py", "-s", "--verbose"])
        #指定某一个用例
        # pytest.main(["../pytest_example/pytest_traditionByClass.py::TestExample::test_div_02", "-s", "--verbose"])
        # 指定包含有特定的内容,目录和模块最好使用test开头 v显示显示详细信息, 执行测试方法包换wxf的测试用例
        # pytest.main(["../pytest_example","-k","wxf","-sv","--verbose"])
        # 运行smoke 测试用例
        # pytest.main(["../test_cases","-m","error and not smoke","-sv","--verbose"])
        pass

    def os_runner(self):
        abspath = os.path.abspath(".") # 获取当前位置绝对路径
        abspath2 = os.path.abspath(__file__) # 获取当前文件的所在的绝对路径 - pytest_perform.py
        basename = os.path.basename(abspath) # 获取当前文件的主名字而不包括扩展名
        file_split = os.path.split(__file__) # 获取文件绝对路径以及本文件名字
        ROOT_DIR1 = os.path.dirname(os.path.abspath(__file__))
        ROOT_DIR2 = os.path.dirname(sys.modules['__main__'].__file__)
        # 找到项目的主目录入口,是由本文件在目录结构中的位置决定的,也就是决定了向上几层
        ROOT_DIR3 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        sys.path.append(ROOT_DIR3) # 临时添加pythonpath中,退出后不在存在
        os.system("pytest -s --verbose ")


if __name__ == '__main__':
    mytest = PytestRunner()
    # mytest.os_runner()
    mytest.pytest_runner()

conftest.py作用

一个测试工程下是可以有多个conftest.py的文件,一般在工程根目录放一个conftest.py起到全局作用。在不同的测试子目录也可以放conftest.py,作用范围只在该层级以及以下目录生效。

pytest Report 

HTML Report

pip install pytest-html -i https://mirrors.aliyun.com/pypi/simple/

生成报告

pytest.main(["../test_cases_pytest",
                     "-k","test_zdemo","-s",
                     "--html=../report/report.html", "--self-contained-html",
                     "--capture=sys"])

pytest-html添加截图

import pytest
# from selenium import webdriver
from mydriver.mydriver import Mydriver
from py._xmlgen import html

_browser = None


@pytest.fixture()
def driver():
    mydriver = Mydriver()
    global _browser
    _browser = mydriver.driver
    yield _browser
    _browser.quit()


def _capture_screenshot():
    '''截图保存为base64'''
    # driver.get_screenshot_as_base64
    # print(driver, "------------------------")
    if _browser:
        return _browser.get_screenshot_as_base64()


@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
    """当测试失败的时候,自动截图,展示到html报告中"""
    pytest_html = item.config.pluginmanager.getplugin('html')
    outcome = yield
    report = outcome.get_result()
    extra = getattr(report, 'extra', [])
    if report.when == 'call' or report.when == "setup":
        xfail = hasattr(report, 'wasxfail')
        if (report.skipped and xfail) or (report.failed and not xfail):
            file_name = report.nodeid.replace("::", "_") + ".png"
            screen_img = _capture_screenshot()
            # screen_img = driver.get_screenshot_as_base64()
            if file_name:
                html = '<div><img src="https://img-blog.csdnimg.cn/2022010701235748897.png" alt="screenshot" style="width:600px;height:300px;" ' \
                       'onclick="window.open(this.src)" align="right"/></div>' % screen_img
                extra.append(pytest_html.extras.html(html))
        report.extra = extra

pytest-allure报告

安装Allure Pytest Plugin

pip install allure-pytest -i https://mirrors.aliyun.com/pypi/simple/

 生成测试报告数据和报告

第一步,生成测试报告数据

在py.test执行测试的时候,指定–alluredir选项及结果数据保存的目录:

py.test test/ --alluredir ./result/
./result/中保存了本次测试的结果数据。另外,还可以执行指定features或者stories执行一部分测试用例,比如执行”购物车功能“下的”加入购物车“子功能的测试用例:

py.test test/ --allure_features='购物车功能' --allure_stories='加入购物车'
第二步,生成测试报告页面

通过下面的命令将./result/目录下的测试数据生成测试报告页面:

allure generate ./result/ -o ./report/ --clean
–clean选项目的是先清空测试报告目录,再生成新的测试报告。

下载allure程序

https://github.com/allure-framework/allure2/releases

配置环境变量

  • 将 allure.bat所在路径配置到系统环境变量path
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值