pytest学习笔记

pytest是python的第三方测试框架,是基于unittest的扩展框架,比unittest更简洁,更高效。
pytest是可以兼容unittest脚本的,unittest测试用例可以通过pytest框架去运行。

编写规则
  1. 测试文件以test_开头(以_test结尾也可以)
  2. 测试类以Test开头,并且不能带有 init 方法
  3. 测试函数以test_开头
  4. 断言使用基本的assert即可
一个最简单的案例

编辑下面代码,然后在命令行中执行命令pytest,会自动在当前目录下搜索符合上面规则的文件名test_demo.py及函数test_demo,然后去进行运行测试。

事例代码如下: test_demo.py


def demo(a):
    return a + 1
    
def test_demo():
    assert demo(1) == 3

执行后会发现测试的结果是错误,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKl6NMaA-1667317222718)(en-resource://database/1934:1)]

测试类案例

会执行该类下所有test_*的函数。

class TestDemo:
    def test_demo1(self):
        assert 1 == 2
        
    def test_demo2(self):
        assert 1 == 1
        
    def test_demo3(self):
        assert 1 == 3

结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6slqMXm-1667317222724)(en-resource://database/1936:1)]

pytest 插件
使用pytest生成html测试报告

安装

pip install pytest-HTML

运行

pytest -q --html=report_test.html

异常测试

执行test_mytest函数,判断f()会抛出SystemExit异常

def f():
    raise SystemExit(1)

def test_mytest():
    with pytest.raises(SystemExit):
        f()
参数化
执行用例规则
  1. 执行该文件夹所有测试用例.

pytest 文件夹/

  1. 执行该脚本中所有测试用例

pytest 脚本名称

  1. -k 按关键字匹配,运行包含该字符串匹配的测试用例

pytest -k "class or filename or function "

  1. -x 遇到错误停止运行

pytest -x

  1. –maxfail=num, 遇到给定次数的错误时,停止测试

pytest --maxfail=3

Fixtures

依赖注入
Fixtures作为函数参数

测试函数可以接收fixture对象作为输入参数,使用@pytest.fixture

test_demo.py
SMTP实例是由smtp_connection创建,test_echlosmtp_connection作为参数。

import pytest

@pytest.fixture
def smtp_connection():
    import smtplib
    return smtplib.SMTP(host='smtp.qq.com', port=587, timeout=5)

def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert 0
使用装饰器的方式使用fixture
import pytest

@pytest.fixture()
def init_data():
    print("1111")
    yield
    print("222")

@pytest.mark.usefixtures('init_data')
def test_demo():
    print("333")
    assert 0
    print("444")

共享fixture函数: conftest.py

在多个测试文件需要用到同一个fixture函数,可以将该函数放到conftest.py中,pytest会自动的将该文件中获取。

测试数据

想要获取可用的测试数据时,可以使用fuxture加载到测试中,pytest有自动缓存机制。

另外有一些插件使用:
pytest-datadir和pytest-datafiles

scope

在指定范围内共享future实例

scope简介
session在一次Run或Debug中执行的所有case共享一个session,第一个case开始执行的时候session开始,最后一个case执行结束的时候session结束,这些case可能分布在不同的class或module中。
module一个.py文件可以看作一个module,其表示的范围指该文件中第一个case到最后一个case之间的范围
class表示的范围即class的范围
function表示的范围即function的范围
scope案例

是一个module的案例。
conftest.py

@pytest.fixture(scope="module")
def smtp():
    return smtplib.SMTP("smtp.qq.com", 587, timeout=5)

test_demo.py

这两个使用的是同一个smpt,都在module的范围内

def test_ehlo(smtp):
    response, msg = smtp.ehlo()
    assert response == 250
    assert b"smtp.qq.com" in msg
    assert 0

def test_noop(smtp):
    response, msg = smtp.noop()
    assert response == 250
    assert 0

test_demo2.py

这个得到的smpttest_demo.py的两个方法中的smpt不一样,因为两个文件是两个module范围。

def test_demo(smtp):
    response, msg = smtp.ehlo()
    assert response == 250
    assert b"smtp.qq.com" in msg
    assert 0
yield 替代 return

所有yield后的代码可以用来释放资源

conftest.py

在module范围内最后一个test执行结束后,才会调用yield之后的代码print('teardown smtp') smtp_connection.close()


import pytest
import smtplib

@pytest.fixture(scope='module')
def smtp_connection():
    smtp_connection = smtplib.SMTP(host='smtp.qq.com', port=587, timeout=5)
    yield smtp_connection
    print('teardown smtp')  
    smtp_connection.close()

注意: 在test中出错后,抛出异常,不会执行yield之后的代码,所以这种方法还不是很完美

使用 with 替代 yield

conftest.py

@pytest.fixture(scope='module')def smtp_connection():
    with smtplib.SMTP(host='smtp.qq.com', port=587, timeout=5) as smtp_connection:
        yield smtp_connection

request

fixture可以传入一个request参数,该参数中包含fixture的一些函数信息
request.addfinalizer() 方法和yield类似,但是,就算test抛出异常,也会关闭所有资源。
request.param 参数化, 每个参数都会执行一次测试

conftest.py

@pytest.fixture(scope="module",params=["smtp.qq.com", "mail.python.org"])
def smtp(request):
    smtp = smtplib.SMTP(request.param, 587, timeout=5)
    def fin():
        print("执行结束!")
        smtp.close()
    def fin1():
        print('this ok!')
    request.addfinalizer(fin)
    request.addfinalizer(fin1) #可以注册多个 功能比 yield 强大!
    return smtp

test_demo.py

def test_demo(smpt):
    assert 0

setup/teardown

module级别

如果有多个test函数或test类在一个模块内,可以选择的实现setup_module和teardown_module,一般模块内的所有函数都会调用一次

def setup_module(module):
    pass

def teardown_module(module):
    pass
class 级别
@classmethod
def setup_class(cls):
    pass

@classmethod
def teardown_class(cls):
    pass
method 级别
def setup_method(self, method):
    pass

def teardown_method(self, method):
    pass

function 级别
def setup_function(function):
    pass

def teardown_function(function):
    pass

参数化

使用pytest.mark.parametrize进行参数化配置
可以对test传入多个条件和期待的结果进行测试。


from datetime import datetime, timedelta
import pytest
testdata = [
    (datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1)),
    (datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1)),
]

@pytest.mark.parametrize("a,b,expected", testdata, ids=["forward", "backward"])
def test_timedistance_v0(a, b, expected):
    diff = a - b
    assert diff == expected
    assert 0

mock

该库可以将测试所以来的对象替换成虚拟对象的库,并且虚拟对象可以在事后进行查看。

patch

通过patch模拟os.remove方法,然后调用,断言该方法被调用过一次,并且该方法并不会真正的执行。


import pytest
import os

class UnixFS:

    @staticmethod
    def rm(filename):
        os.remove(filename)

    def test_unix_fs(mocker):
        mocker.patch('os.remove')
        UnixFS.rm("test.txt")
        os.remove.assert_called_once_with('test.txt')

可以使用装饰器的方式使用patch

import mock

class UnixFS:
    @staticmethod
    def rm(filename):
        os.remove(filename)

@mock.patch('os.remove')
def test_unix_fs(rm):
    UnixFS.rm("test.txt")
    os.remove.assert_called_once_with('test.txt')

spy

spybar方法和Foo中的bar方法功能执行一直,并且可以使用模拟功能,如计算被调用次数。


def test_spy(mocker):
    class Foo(object):
        def bar(self):
            return 42

    foo = Foo()
    mocker.spy(foo, 'bar')
    assert foo.bar() == 42
    assert foo.bar.call_count == 1

自定义标记mark

可以对指定的test进行标记,规定哪些test跑,哪些test不跑


import pytest

@pytest.mark.runtest
def test_run():
    print("run")

def test_not_run():
    print("not run")

只有标记为runtest才跑

pytest -m=runtest

欢迎关注,互相学习,共同进步~

我的个人博客

我的微信公众号:编程黑洞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值