IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> pytest测试框架 -> 正文阅读

[开发测试]pytest测试框架

调用

在模块中运行测试

pytest test_mod.py

在目录中运行测试

pytest testing/

通过关键字表达式运行测试

pytest -k "MyClass and not method"

按节点 ID 运行测试

每个收集到的测试都被分配了一个唯一的nodeid,它由模块文件名后跟类名、函数名和参数化参数等说明::符组成,用字符分隔。

要在模块中运行特定测试:

pytest test_mod.py::test_func
pytest test_mod.py::TestClass::test_method

通过标记表达式运行测试

pytest -m slow

将运行所有用@pytest.mark.slow装饰器装饰的测试。

从 Python 代码调用 pytest

pytest.main()

pytest 使用 . 标识测试成功(PASSED)

Pytest 查找测试策略

默认情况下,pytest 会递归查找当前目录下所有以 test 开始或结尾的 Python 脚本,并执行文件内的所有以 test 开始或结束的函数和方法。

第一种:指定函数名

pytest 	/test_no_mark.py::test_func1

第二种:模糊匹配

pytest -k raise test_sample.py 

第三种,使用 pytest.mark 在函数上进行标记。

@pytest.mark.finished
def test_func1():
    assert 1 == 1

@pytest.mark.unfinished
def test_func2():
    assert 1 != 1
pytest -m finished test_sample.py 

跳过测试

@pytest.mark.skip()
def test_connect():
    pass
pytest 使用 s 表示测试被跳过(SKIPPED)。

Pytest 使用 pytest.mark.xfail 实现预见错误功能

@pytest.mark.xfail(gen.__version__ < '0.2.0',
                   reason='not supported until v0.2.0')
def test_api():
    id_1 = gen.unique_id()
    id_2 = gen.unique_id()
    assert id_1 != id_2

pytest 使用 x 表示预见的失败(XFAIL)。

创建第一个测试用例
def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5

命令行输入pytest运行,[100%]指运行所有测试案例的全面进步。完成后, pytest 会显示失败报告,因为func(3)不返回5.

Python 测试发现的约定

pytestrootdir为每个测试运行确定一个,这取决于命令行参数(指定的测试文件、路径)和配置文件的存在

--rootdir=path命令行选项可以用来强制一个特定的目录。请注意,与其他命令行选项相反,--rootdir不能与addoptsinside一起使用, pytest.ini因为rootdir已经用于查找 pytest.ini

断言某个异常被引发
import pytest
def f():
    raise SyntaxError(1)


def test_raise():
    with pytest.raises(SyntaxError):
        f()	
        
def test_raises():
    with pytest.raises(TypeError) as e:
        connect('localhost', '6379')
    exec_msg = e.value.args[0]
    assert exec_msg == 'port type must be int'

以“安静”报告模式执行测试功能 pytest -q test_sample.py

可以使用 -v 选项,显示测试的详细信息。

使用 pytest -h 查看 pytest 的所有选项。

pytest 使用 F 标识测试失败(FAILED)。

在类中分组多个测试
class TestClass(object):
    def test_one(self):
        x = "this"
        assert 'h' in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, 'check')

pytest发现遵循其Python 测试发现约定的所有测试,因此它会找到两个带test_前缀的函数。不需要子类化任何东西,但请确保在您的类前加上前缀,Test否则该类将被跳过。

在类内对测试进行分组时需要注意的是,每个测试都有一个唯一的类实例。

参数测试化

在 pytest 中,我们有更好的解决方法,就是参数化测试,即每组参数都独立执行一次测试。使用的工具就是 pytest.mark.parametrize(argnames, argvalues)

@pytest.mark.parametrize('passwd',
                      ['123456',
                       'abcdefdfs',
                       'as52345fasdf4'])
def test_passwd_length(passwd):
    assert len(passwd) >= 8

Fixture

Fixture是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们,Pytest 使用 pytest.fixture() 定义fixture

Pytest 使用文件 conftest.py 集中管理固件。

不要自己显式调用 conftest.py,pytest 会自动调用,可以把 conftest 当做插件来理解。

预处理和后处理

Pytest 使用 yield 关键词将固件分为两部分,yield 之前的代码属于预处理,会在测试前执行;yield 之后的代码属于后处理,将在测试完成后执行。

例如在执行测试前后连接关闭数据库

@pytest.fixture()
def db():
    print('Connection successful')

    yield

    print('Connection closed')


def search_user(user_id):
    d = {
        '001': 'xiaoming'
    }
    print("stop...")
    return d[user_id]


def test_search(db):
    assert search_user('001') == 'xiaoming'

如果想更细的跟踪固件执行,可以使用 --setup-show 选项

fixture作用域

分为函数级 类级 模块级 会话级

@pytest.fixture(scope='function')
def func_scope():
    pass


@pytest.fixture(scope='module')
def mod_scope():
    pass


@pytest.fixture(scope='session')
def sess_scope():
    pass

执行顺序 为会话级 模块级 函数级

对于类使用作用域要使用pytest.mark.usefixtures

@pytest.mark.usefixtures('class_scope')
class TestClassScope:
    def test_1(self):
        pass

    def test_2(self):
        pass

fixture自动执行

加参数autouse

import pytest
import time

DATE_FORMAT = '%Y-%m-%d %H:%M:%S'


@pytest.fixture(scope='session', autouse=True)
def timer_session_scope():
    start = time.time()
    print('\nstart: {}'.format(time.strftime(DATE_FORMAT, time.localtime(start))))

    yield

    finished = time.time()
    print('finished: {}'.format(time.strftime(DATE_FORMAT, time.localtime(finished))))
    print('Total time cost: {:.3f}s'.format(finished - start))


@pytest.fixture(autouse=True)
def timer_function_scope():
    start = time.time()
    yield
    print(' Time cost: {:.3f}s'.format(time.time() - start))


def test_1():
    print("test_1")
    time.sleep(1)


def test_2():
    print("test_2")
    time.sleep(2)
#自动执行运行顺序,开始时先是session级别执行,结束时则反过来执行。

fixture重命名

@pytest.fixture(name="age")
def age():
    return 1


def test_age(age):
    assert age == 2

fixtrue参数化

需借助request

@pytest.fixture(params=[('redis', '6379'), ('elasticsearch', '9200')])
def param(request):
    return request.param


@pytest.fixture(autouse=True)
def db(param):
    print('\nSucceed to connect %s:%s' % param)

    yield

    print('\nSucceed to close %s:%s' % param)


def test_api():
    assert 1 == 1

与函数参数化使用 @pytest.mark.parametrize 不同,fixtrue在定义时使用 params 参数进行参数化。

固件参数化依赖于内置固件 request 及其属性 param

内置固件

tmpdir & tmpdir_factory

用于临时文件和目录管理,默认会在测试结束时删除。

tmpdir 只有 function 作用域,只能在函数内使用。

使用 tmpdir.mkdir() 创建目临时录,tmpdir.join() 创建临时文件(或者使用创建的目录)。

def test_tmpdir(tmpdir):
    a_dir = tmpdir.mkdir('mytmpdir')
    a_file = a_dir.join('tmpfile.txt')

    a_file.write('hello, pytest!')

    assert a_file.read() == 'hello, pytest!'

tmpdir_factory 可以在所有作用域使用,包括 function, class, module, session

@pytest.fixture(scope='module')
def my_tmpdir_factory(tmpdir_factory):
    a_dir = tmpdir_factory.mktemp('mytmpdir')
    a_file = a_dir.join('tmpfile.txt')

    a_file.write('hello, pytest!')

    return a_file

pytestconfig

使用 pytestconfig,可以很方便的读取命令行参数和配置文件。

# conftest.py

def pytest_addoption(parser):
    parser.addoption('--host', action='store', default="127.0.0.1",
                     help='host of db')
    parser.addoption('--port', action='store', default='8888',
                     help='port of db')

然后就可以在测试函数中通过 pytestconfig 获取命令行参数:

# test_config.py

def test_option1(pytestconfig):
    print('host: %s' % pytestconfig.getoption('host'))
    print('port: %s' % pytestconfig.getoption('port'))

pytestconfig 其实是 request.config 的快捷方式,所以也可以自定义固件实现命令行参数读取。

@pytest.fixture
def config(request):
    return request.config


# test_config.py

def test_option2(config):
    print('host: %s' % config.getoption('host'))
    print('port: %s' % config.getoption('port'))

capsys

capsys 用于捕获 stdoutstderr 的内容,并临时关闭系统输出。

# test_capsys.py

def ping(output):
    print('Pong...', file=output)


def test_stdout(capsys):
    ping(sys.stdout)
    out, err = capsys.readouterr()
    assert out == 'Pong...\n'
    assert err == ''


def test_stderr(capsys):
    ping(sys.stderr)
    out, err = capsys.readouterr()
    assert out == ''
    assert err == 'Pong...\n'

monkeypatch

monkeypath 用于运行时动态修改类或模块。

monkey patch这个术语仅指在运行时对类或模块进行的动态修改,其动机是为现有的第三方代码打补丁

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return "ook ook eee eee eee!"

SomeClass.speak = speak

Pytest 内置 monkeypatch 提供的函数有:

  • setattr(target, name, value, raising=True),设置属性;
  • delattr(target, name, raising=True),删除属性;
  • setitem(dic, name, value),字典添加元素;
  • delitem(dic, name, raising=True),字典删除元素;
  • setenv(name, value, prepend=None),设置环境变量;
  • delenv(name, raising=True),删除环境变量;
  • syspath_prepend(path),添加系统路径;
  • chdir(path),切换目录。

下面使用保存配置文件示例说明 monkeypatch 的作用和使用。

假设我们需要切换某个服务到国内科大源以加速,有以下脚本用于修改配置文件 .conf.json

# test_monkeypatch.py

def dump_config(config):
    path = os.path.expanduser('~/.conf.json')
    with open(path, 'w', encoding='utf-8') as wr:
        json.dump(config, wr, indent=4)


def test_config():
    dump_config(config)
    path = os.path.expanduser('~/.conf.json')
    expected = json.load(open(path, 'r', encoding='utf-8'))
    assert expected == config

recwarn

recwarn 用于捕获程序中 warnings 产生的警告。

def warn():
    warnings.warn('Deprecated function', DeprecationWarning)


def test_warn(recwarn):
    warn()
    assert len(recwarn) == 1
    w = recwarn.pop()
    assert w.category == DeprecationWarning

此外,pytest 可以使用 pytest.warns() 捕获警告:

def test_warn2():
    with pytest.warns(None) as warnings:
        warn()

    assert len(warnings) == 1
    w = warnings.pop()
    assert w.category == DeprecationWarning
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:30:55  更:2021-08-03 11:31:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 20:47:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码