目录

Pytest Fixture Note

最近在学习实践自动化相关的知识,最终选用 pytest 来组织测试用例。

本文是 pytest 学习笔记的第一篇。

Fixture 是 pytest 中的一个基本概念,可以简单理解为在测试用例前需要执行的内容,我用来初始化环境、准备数据等工作。

Fixture

在被当做 fixture 的函数前面加上 @pytest.fixture来定义一个 Fixture

1
2
3
@pytest.fixture()
def before():
    print('\nbefore each test')

Scope

  • function:每个 test 都运行,默认是 function 的 scope
  • class:每个 class 的所有 test 只运行一次
  • module:每个 module 的所有 test 只运行一次
  • session:每个 session 只运行一次
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@pytest.fixture(scope="module")
def smtp():
    smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
    yield smtp
    #yield下面是teardown内容
    smtp.close()

def test_ehlo(smtp):
#fixture的function名称,可以直接作为参数,传给需要使用它的测试样例。 在使用时,smtp并非前面定义的function,而是function的返回值,即smtplib.SMTP
    response, msg = smtp.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg

conftest.py

conftest.py是 pytest 的默认配置文件,可以在其中放公用的 fixture 或 plugin。

1
2
3
4
5
6
7
8
9
tests
├── conftest.py
├── test_a.py
├── test_b.py
└── sub
    ├── __init__.py
    ├── conftest.py
    ├── test_c.py
    └── test_d.py

conftest.py遵守就近原则,会优先使用层级最近的 conftest 中定义的 Fixture。同时外层的测试用例 a,b 不能使用内层conftest.py中定义的 fixture

Use Fixture

1. 当做参数直接调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@pytest.fixture(scope="module")
def smtp():
    smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
    yield smtp
    smtp.close()

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

fixture 的 function 名称,可以直接作为参数,传给需要使用它的测试样例。 在使用时,smtp并非前面定义的 function,而是 function 的返回值,即smtplib.SMTP

2. 在函数前用 Fixture Decorator 调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@pytest.mark.usefixtures("before")
def test_1():
    print('test_1()')

class Test1:
    @pytest.mark.usefixtures("before")
    def test_3(self):
        print('test_1()')

    @pytest.mark.usefixtures("before")
    def test_4(self):
        print('test_2()')

@pytest.mark.usefixtures("before")
class Test2:
    def test_5(self):
        print('test_1()')

    def test_6(self):
        print('test_2()')

3. 用 Autouse 调用 Fixture

fixture decorator 一个 optional 的参数是autouse, 默认设置为 False。 当默认为 False,就可以选择用上面两种方式来试用 fixture。 当设置为 True 时,在一个 session 内的所有的 test 都会自动调用这个 fixture。

1
2
3
@pytest.fixture(autouse=True)
def before():
    print('\nbefore each test')

Finallizer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@pytest.fixture()
def smtp(request):
    smtp = smtplib.SMTP("smtp.gmail.com")
    def fin():
    #释放函数
        print ("teardown smtp")
        smtp.close()
    request.addfinalizer(fin)
    #测试完成后调用
    return smtp

通过addfinallizer()注册释放函数

Parametrizing

fixture 可以通过参数化来循环使用预设的参数

1. params

1
2
3
4
5
6
@pytest.fixture(params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
    smtp = smtplib.SMTP(request.param, 587, timeout=5)
    yield smtp
    print ("finalizing %s" % smtp)
    smtp.close()

@pytest.fixture中,指定参数params,就可以利用特殊对象(request)来引用request.param。 使用以上带参数的 smtp 的测试样例,都会被执行两次。

2. @pytest.mark.parametrize

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def add(a, b):
    return a + b

@pytest.mark.parametrize("test_input, expected", [
    ([1, 1], 2),
    ([2, 2], 4),
    ([0, 1], 1),
])
def test_add(test_input, expected):
    assert expected == add(test_input[0], test_input[1])

Build-in Fixture

pytest --fixtures可以列出所有可用的 fixture,包括内置的、插件中的、以及当前项目定义的。

capsys

capsys可以捕捉测试 function 的标准输出

1
2
3
4
def test_print(capsys):
    print('hello')
    out, err = capsys.readouterr()
    assert 'hello' == out

tmpdir

tmpdir则可以自动创建临时文件夹

1
2
3
4
5
def test_path(tmpdir):
    from py._path.local import LocalPath
    assert isinstance(tmpdir, LocalPath)
    from os.path import isdir
    assert isdir(str(tmpdir))

参考